/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* vim: set ts=2 sw=2 et tw=78: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"nsLayoutUtils.h"#include"mozilla/ArrayUtils.h"#include"mozilla/BasicEvents.h"#include"mozilla/ClearOnShutdown.h"#include"mozilla/EffectCompositor.h"#include"mozilla/EffectSet.h"#include"mozilla/EventDispatcher.h"#include"mozilla/FloatingPoint.h"#include"mozilla/gfx/gfxVars.h"#include"mozilla/gfx/PathHelpers.h"#include"mozilla/layers/PAPZ.h"#include"mozilla/Likely.h"#include"mozilla/Maybe.h"#include"mozilla/MemoryReporting.h"#include"mozilla/dom/ContentChild.h"#include"mozilla/Unused.h"#include"nsCharTraits.h"#include"nsDocument.h"#include"nsFontMetrics.h"#include"nsPresContext.h"#include"nsIContent.h"#include"nsIDOMHTMLDocument.h"#include"nsIDOMHTMLElement.h"#include"nsFrameList.h"#include"nsGkAtoms.h"#include"nsIAtom.h"#include"nsCaret.h"#include"nsCSSPseudoElements.h"#include"nsCSSAnonBoxes.h"#include"nsCSSColorUtils.h"#include"nsView.h"#include"nsViewManager.h"#include"nsPlaceholderFrame.h"#include"nsIScrollableFrame.h"#include"nsIDOMEvent.h"#include"nsDisplayList.h"#include"nsRegion.h"#include"nsFrameManager.h"#include"nsBlockFrame.h"#include"nsBidiPresUtils.h"#include"imgIContainer.h"#include"ImageOps.h"#include"ImageRegion.h"#include"gfxRect.h"#include"gfxContext.h"#include"gfxContext.h"#include"nsIInterfaceRequestorUtils.h"#include"nsCSSRendering.h"#include"nsTextFragment.h"#include"nsThemeConstants.h"#include"nsPIDOMWindow.h"#include"nsIDocShell.h"#include"nsIWidget.h"#include"gfxMatrix.h"#include"gfxPrefs.h"#include"gfxTypes.h"#include"nsTArray.h"#include"mozilla/dom/HTMLCanvasElement.h"#include"nsICanvasRenderingContextInternal.h"#include"gfxPlatform.h"#include<algorithm>#include<limits>#include"mozilla/dom/AnonymousContent.h"#include"mozilla/dom/HTMLVideoElement.h"#include"mozilla/dom/HTMLImageElement.h"#include"mozilla/dom/DOMRect.h"#include"mozilla/dom/DOMStringList.h"#include"mozilla/dom/KeyframeEffectReadOnly.h"#include"mozilla/layers/APZCCallbackHelper.h"#include"imgIRequest.h"#include"nsIImageLoadingContent.h"#include"nsCOMPtr.h"#include"nsCSSProps.h"#include"nsListControlFrame.h"#include"mozilla/dom/Element.h"#include"nsCanvasFrame.h"#include"gfxDrawable.h"#include"gfxEnv.h"#include"gfxUtils.h"#include"nsDataHashtable.h"#include"nsTableWrapperFrame.h"#include"nsTextFrame.h"#include"nsFontFaceList.h"#include"nsFontInflationData.h"#include"nsSVGUtils.h"#include"SVGImageContext.h"#include"SVGTextFrame.h"#include"nsStyleStructInlines.h"#include"nsStyleTransformMatrix.h"#include"nsIFrameInlines.h"#include"ImageContainer.h"#include"nsComputedDOMStyle.h"#include"ActiveLayerTracker.h"#include"mozilla/gfx/2D.h"#include"gfx2DGlue.h"#include"mozilla/LookAndFeel.h"#include"UnitTransforms.h"#include"TiledLayerBuffer.h" // For TILEDLAYERBUFFER_TILE_SIZE#include"ClientLayerManager.h"#include"nsRefreshDriver.h"#include"nsIContentViewer.h"#include"LayersLogging.h"#include"mozilla/Preferences.h"#include"nsFrameSelection.h"#include"FrameLayerBuilder.h"#include"mozilla/layers/APZCTreeManager.h"#include"mozilla/layers/CompositorBridgeChild.h"#include"mozilla/Telemetry.h"#include"mozilla/EventDispatcher.h"#include"mozilla/EventStateManager.h"#include"mozilla/RuleNodeCacheConditions.h"#include"mozilla/StyleAnimationValue.h"#include"mozilla/StyleSetHandle.h"#include"mozilla/StyleSetHandleInlines.h"#include"RegionBuilder.h"#include"SVGSVGElement.h"#include"DisplayItemClip.h"#include"mozilla/layers/WebRenderLayerManager.h"#ifdef MOZ_XUL#include"nsXULPopupManager.h"#endif#include"GeckoProfiler.h"#include"nsAnimationManager.h"#include"nsTransitionManager.h"#include"mozilla/RestyleManager.h"#include"mozilla/RestyleManagerInlines.h"#include"LayoutLogging.h"// Make sure getpid() works.#ifdef XP_WIN#include<process.h>#define getpid _getpid#else#include<unistd.h>#endifusingnamespacemozilla;usingnamespacemozilla::dom;usingnamespacemozilla::image;usingnamespacemozilla::layers;usingnamespacemozilla::layout;usingnamespacemozilla::gfx;#define GRID_ENABLED_PREF_NAME "layout.css.grid.enabled"#define GRID_TEMPLATE_SUBGRID_ENABLED_PREF_NAME "layout.css.grid-template-subgrid-value.enabled"#define WEBKIT_PREFIXES_ENABLED_PREF_NAME "layout.css.prefixes.webkit"#define TEXT_ALIGN_UNSAFE_ENABLED_PREF_NAME "layout.css.text-align-unsafe-value.enabled"#define FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME "layout.css.float-logical-values.enabled"// The time in number of frames that we estimate for a refresh driver// to be quiescent#define DEFAULT_QUIESCENT_FRAMES 2// The time (milliseconds) we estimate is needed between the end of an// idle time and the next Tick.#define DEFAULT_IDLE_PERIOD_TIME_LIMIT 1.0f#ifdef DEBUG// TODO: remove, see bug 598468.boolnsLayoutUtils::gPreventAssertInCompareTreePosition=false;#endif // DEBUGtypedefFrameMetrics::ViewIDViewID;typedefnsStyleTransformMatrix::TransformReferenceBoxTransformReferenceBox;/* static */uint32_tnsLayoutUtils::sFontSizeInflationEmPerLine;/* static */uint32_tnsLayoutUtils::sFontSizeInflationMinTwips;/* static */uint32_tnsLayoutUtils::sFontSizeInflationLineThreshold;/* static */int32_tnsLayoutUtils::sFontSizeInflationMappingIntercept;/* static */uint32_tnsLayoutUtils::sFontSizeInflationMaxRatio;/* static */boolnsLayoutUtils::sFontSizeInflationForceEnabled;/* static */boolnsLayoutUtils::sFontSizeInflationDisabledInMasterProcess;/* static */uint32_tnsLayoutUtils::sSystemFontScale;/* static */uint32_tnsLayoutUtils::sZoomMaxPercent;/* static */uint32_tnsLayoutUtils::sZoomMinPercent;/* static */boolnsLayoutUtils::sInvalidationDebuggingIsEnabled;/* static */boolnsLayoutUtils::sInterruptibleReflowEnabled;/* static */boolnsLayoutUtils::sSVGTransformBoxEnabled;/* static */boolnsLayoutUtils::sTextCombineUprightDigitsEnabled;#ifdef MOZ_STYLO/* static */boolnsLayoutUtils::sStyloEnabled;#endif/* static */boolnsLayoutUtils::sStyleAttrWithXMLBaseDisabled;/* static */uint32_tnsLayoutUtils::sIdlePeriodDeadlineLimit;/* static */uint32_tnsLayoutUtils::sQuiescentFramesBeforeIdlePeriod;staticViewIDsScrollIdCounter=FrameMetrics::START_SCROLL_ID;typedefnsDataHashtable<nsUint64HashKey,nsIContent*>ContentMap;staticContentMap*sContentMap=nullptr;staticContentMap&GetContentMap(){if(!sContentMap){sContentMap=newContentMap();}return*sContentMap;}// When the pref "layout.css.grid.enabled" changes, this function is invoked// to let us update kDisplayKTable, to selectively disable or restore the// entries for "grid" and "inline-grid" in that table.staticvoidGridEnabledPrefChangeCallback(constchar*aPrefName,void*aClosure){MOZ_ASSERT(strncmp(aPrefName,GRID_ENABLED_PREF_NAME,ArrayLength(GRID_ENABLED_PREF_NAME))==0,"We only registered this callback for a single pref, so it ""should only be called for that pref");staticint32_tsIndexOfGridInDisplayTable;staticint32_tsIndexOfInlineGridInDisplayTable;staticboolsAreGridKeywordIndicesInitialized;// initialized to falseboolisGridEnabled=Preferences::GetBool(GRID_ENABLED_PREF_NAME,false);if(!sAreGridKeywordIndicesInitialized){// First run: find the position of "grid" and "inline-grid" in// kDisplayKTable.sIndexOfGridInDisplayTable=nsCSSProps::FindIndexOfKeyword(eCSSKeyword_grid,nsCSSProps::kDisplayKTable);MOZ_ASSERT(sIndexOfGridInDisplayTable>=0,"Couldn't find grid in kDisplayKTable");sIndexOfInlineGridInDisplayTable=nsCSSProps::FindIndexOfKeyword(eCSSKeyword_inline_grid,nsCSSProps::kDisplayKTable);MOZ_ASSERT(sIndexOfInlineGridInDisplayTable>=0,"Couldn't find inline-grid in kDisplayKTable");sAreGridKeywordIndicesInitialized=true;}// OK -- now, stomp on or restore the "grid" entries in kDisplayKTable,// depending on whether the grid pref is enabled vs. disabled.if(sIndexOfGridInDisplayTable>=0){nsCSSProps::kDisplayKTable[sIndexOfGridInDisplayTable].mKeyword=isGridEnabled?eCSSKeyword_grid:eCSSKeyword_UNKNOWN;}if(sIndexOfInlineGridInDisplayTable>=0){nsCSSProps::kDisplayKTable[sIndexOfInlineGridInDisplayTable].mKeyword=isGridEnabled?eCSSKeyword_inline_grid:eCSSKeyword_UNKNOWN;}}// When the pref "layout.css.prefixes.webkit" changes, this function is invoked// to let us update kDisplayKTable, to selectively disable or restore the// entries for "-webkit-box" and "-webkit-inline-box" in that table.staticvoidWebkitPrefixEnabledPrefChangeCallback(constchar*aPrefName,void*aClosure){MOZ_ASSERT(strncmp(aPrefName,WEBKIT_PREFIXES_ENABLED_PREF_NAME,ArrayLength(WEBKIT_PREFIXES_ENABLED_PREF_NAME))==0,"We only registered this callback for a single pref, so it ""should only be called for that pref");staticint32_tsIndexOfWebkitBoxInDisplayTable;staticint32_tsIndexOfWebkitInlineBoxInDisplayTable;staticint32_tsIndexOfWebkitFlexInDisplayTable;staticint32_tsIndexOfWebkitInlineFlexInDisplayTable;staticboolsAreKeywordIndicesInitialized;// initialized to falseboolisWebkitPrefixSupportEnabled=Preferences::GetBool(WEBKIT_PREFIXES_ENABLED_PREF_NAME,false);if(!sAreKeywordIndicesInitialized){// First run: find the position of the keywords in kDisplayKTable.sIndexOfWebkitBoxInDisplayTable=nsCSSProps::FindIndexOfKeyword(eCSSKeyword__webkit_box,nsCSSProps::kDisplayKTable);MOZ_ASSERT(sIndexOfWebkitBoxInDisplayTable>=0,"Couldn't find -webkit-box in kDisplayKTable");sIndexOfWebkitInlineBoxInDisplayTable=nsCSSProps::FindIndexOfKeyword(eCSSKeyword__webkit_inline_box,nsCSSProps::kDisplayKTable);MOZ_ASSERT(sIndexOfWebkitInlineBoxInDisplayTable>=0,"Couldn't find -webkit-inline-box in kDisplayKTable");sIndexOfWebkitFlexInDisplayTable=nsCSSProps::FindIndexOfKeyword(eCSSKeyword__webkit_flex,nsCSSProps::kDisplayKTable);MOZ_ASSERT(sIndexOfWebkitFlexInDisplayTable>=0,"Couldn't find -webkit-flex in kDisplayKTable");sIndexOfWebkitInlineFlexInDisplayTable=nsCSSProps::FindIndexOfKeyword(eCSSKeyword__webkit_inline_flex,nsCSSProps::kDisplayKTable);MOZ_ASSERT(sIndexOfWebkitInlineFlexInDisplayTable>=0,"Couldn't find -webkit-inline-flex in kDisplayKTable");sAreKeywordIndicesInitialized=true;}// OK -- now, stomp on or restore the "-webkit-{box|flex}" entries in// kDisplayKTable, depending on whether the webkit prefix pref is enabled// vs. disabled.if(sIndexOfWebkitBoxInDisplayTable>=0){nsCSSProps::kDisplayKTable[sIndexOfWebkitBoxInDisplayTable].mKeyword=isWebkitPrefixSupportEnabled?eCSSKeyword__webkit_box:eCSSKeyword_UNKNOWN;}if(sIndexOfWebkitInlineBoxInDisplayTable>=0){nsCSSProps::kDisplayKTable[sIndexOfWebkitInlineBoxInDisplayTable].mKeyword=isWebkitPrefixSupportEnabled?eCSSKeyword__webkit_inline_box:eCSSKeyword_UNKNOWN;}if(sIndexOfWebkitFlexInDisplayTable>=0){nsCSSProps::kDisplayKTable[sIndexOfWebkitFlexInDisplayTable].mKeyword=isWebkitPrefixSupportEnabled?eCSSKeyword__webkit_flex:eCSSKeyword_UNKNOWN;}if(sIndexOfWebkitInlineFlexInDisplayTable>=0){nsCSSProps::kDisplayKTable[sIndexOfWebkitInlineFlexInDisplayTable].mKeyword=isWebkitPrefixSupportEnabled?eCSSKeyword__webkit_inline_flex:eCSSKeyword_UNKNOWN;}}// When the pref "layout.css.text-align-unsafe-value.enabled" changes, this// function is called to let us update kTextAlignKTable & kTextAlignLastKTable,// to selectively disable or restore the entries for "unsafe" in those tables.staticvoidTextAlignUnsafeEnabledPrefChangeCallback(constchar*aPrefName,void*aClosure){NS_ASSERTION(strcmp(aPrefName,TEXT_ALIGN_UNSAFE_ENABLED_PREF_NAME)==0,"Did you misspell "TEXT_ALIGN_UNSAFE_ENABLED_PREF_NAME" ?");staticboolsIsInitialized;staticint32_tsIndexOfUnsafeInTextAlignTable;staticint32_tsIndexOfUnsafeInTextAlignLastTable;boolisTextAlignUnsafeEnabled=Preferences::GetBool(TEXT_ALIGN_UNSAFE_ENABLED_PREF_NAME,false);if(!sIsInitialized){// First run: find the position of "unsafe" in kTextAlignKTable.sIndexOfUnsafeInTextAlignTable=nsCSSProps::FindIndexOfKeyword(eCSSKeyword_unsafe,nsCSSProps::kTextAlignKTable);// First run: find the position of "unsafe" in kTextAlignLastKTable.sIndexOfUnsafeInTextAlignLastTable=nsCSSProps::FindIndexOfKeyword(eCSSKeyword_unsafe,nsCSSProps::kTextAlignLastKTable);sIsInitialized=true;}// OK -- now, stomp on or restore the "unsafe" entry in the keyword tables,// depending on whether the pref is enabled vs. disabled.MOZ_ASSERT(sIndexOfUnsafeInTextAlignTable>=0);nsCSSProps::kTextAlignKTable[sIndexOfUnsafeInTextAlignTable].mKeyword=isTextAlignUnsafeEnabled?eCSSKeyword_unsafe:eCSSKeyword_UNKNOWN;MOZ_ASSERT(sIndexOfUnsafeInTextAlignLastTable>=0);nsCSSProps::kTextAlignLastKTable[sIndexOfUnsafeInTextAlignLastTable].mKeyword=isTextAlignUnsafeEnabled?eCSSKeyword_unsafe:eCSSKeyword_UNKNOWN;}// When the pref "layout.css.float-logical-values.enabled" changes, this// function is called to let us update kFloatKTable & kClearKTable,// to selectively disable or restore the entries for logical values// (inline-start and inline-end) in those tables.staticvoidFloatLogicalValuesEnabledPrefChangeCallback(constchar*aPrefName,void*aClosure){NS_ASSERTION(strcmp(aPrefName,FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME)==0,"Did you misspell "FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME" ?");staticboolsIsInitialized;staticint32_tsIndexOfInlineStartInFloatTable;staticint32_tsIndexOfInlineEndInFloatTable;staticint32_tsIndexOfInlineStartInClearTable;staticint32_tsIndexOfInlineEndInClearTable;boolisFloatLogicalValuesEnabled=Preferences::GetBool(FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME,false);if(!sIsInitialized){// First run: find the position of "inline-start" in kFloatKTable.sIndexOfInlineStartInFloatTable=nsCSSProps::FindIndexOfKeyword(eCSSKeyword_inline_start,nsCSSProps::kFloatKTable);// First run: find the position of "inline-end" in kFloatKTable.sIndexOfInlineEndInFloatTable=nsCSSProps::FindIndexOfKeyword(eCSSKeyword_inline_end,nsCSSProps::kFloatKTable);// First run: find the position of "inline-start" in kClearKTable.sIndexOfInlineStartInClearTable=nsCSSProps::FindIndexOfKeyword(eCSSKeyword_inline_start,nsCSSProps::kClearKTable);// First run: find the position of "inline-end" in kClearKTable.sIndexOfInlineEndInClearTable=nsCSSProps::FindIndexOfKeyword(eCSSKeyword_inline_end,nsCSSProps::kClearKTable);sIsInitialized=true;}// OK -- now, stomp on or restore the logical entries in the keyword tables,// depending on whether the pref is enabled vs. disabled.MOZ_ASSERT(sIndexOfInlineStartInFloatTable>=0);nsCSSProps::kFloatKTable[sIndexOfInlineStartInFloatTable].mKeyword=isFloatLogicalValuesEnabled?eCSSKeyword_inline_start:eCSSKeyword_UNKNOWN;MOZ_ASSERT(sIndexOfInlineEndInFloatTable>=0);nsCSSProps::kFloatKTable[sIndexOfInlineEndInFloatTable].mKeyword=isFloatLogicalValuesEnabled?eCSSKeyword_inline_end:eCSSKeyword_UNKNOWN;MOZ_ASSERT(sIndexOfInlineStartInClearTable>=0);nsCSSProps::kClearKTable[sIndexOfInlineStartInClearTable].mKeyword=isFloatLogicalValuesEnabled?eCSSKeyword_inline_start:eCSSKeyword_UNKNOWN;MOZ_ASSERT(sIndexOfInlineEndInClearTable>=0);nsCSSProps::kClearKTable[sIndexOfInlineEndInClearTable].mKeyword=isFloatLogicalValuesEnabled?eCSSKeyword_inline_end:eCSSKeyword_UNKNOWN;}template<typenameTestType>staticboolHasMatchingAnimations(EffectSet*aEffects,TestType&&aTest){for(KeyframeEffectReadOnly*effect:*aEffects){if(aTest(*effect)){returntrue;}}returnfalse;}template<typenameTestType>staticboolHasMatchingAnimations(constnsIFrame*aFrame,TestType&&aTest){EffectSet*effects=EffectSet::GetEffectSet(aFrame);if(!effects){returnfalse;}returnHasMatchingAnimations(effects,aTest);}boolnsLayoutUtils::HasCurrentTransitions(constnsIFrame*aFrame){returnHasMatchingAnimations(aFrame,[](KeyframeEffectReadOnly&aEffect){// Since |aEffect| is current, it must have an associated Animation// so we don't need to null-check the result of GetAnimation().returnaEffect.IsCurrent()&&aEffect.GetAnimation()->AsCSSTransition();});}staticboolMayHaveAnimationOfProperty(EffectSet*effects,nsCSSPropertyIDaProperty){MOZ_ASSERT(effects);if(aProperty==eCSSProperty_transform&&!effects->MayHaveTransformAnimation()){returnfalse;}if(aProperty==eCSSProperty_opacity&&!effects->MayHaveOpacityAnimation()){returnfalse;}returntrue;}boolnsLayoutUtils::HasAnimationOfProperty(EffectSet*aEffectSet,nsCSSPropertyIDaProperty){if(!aEffectSet||!MayHaveAnimationOfProperty(aEffectSet,aProperty)){returnfalse;}returnHasMatchingAnimations(aEffectSet,[&aProperty](KeyframeEffectReadOnly&aEffect){return(aEffect.IsInEffect()||aEffect.IsCurrent())&&aEffect.HasAnimationOfProperty(aProperty);});}boolnsLayoutUtils::HasAnimationOfProperty(constnsIFrame*aFrame,nsCSSPropertyIDaProperty){returnHasAnimationOfProperty(EffectSet::GetEffectSet(aFrame),aProperty);}boolnsLayoutUtils::HasEffectiveAnimation(constnsIFrame*aFrame,nsCSSPropertyIDaProperty){EffectSet*effects=EffectSet::GetEffectSet(aFrame);if(!effects||!MayHaveAnimationOfProperty(effects,aProperty)){returnfalse;}returnHasMatchingAnimations(effects,[&aProperty](KeyframeEffectReadOnly&aEffect){return(aEffect.IsInEffect()||aEffect.IsCurrent())&&aEffect.HasEffectiveAnimationOfProperty(aProperty);});}staticfloatGetSuitableScale(floataMaxScale,floataMinScale,nscoordaVisibleDimension,nscoordaDisplayDimension){floatdisplayVisibleRatio=float(aDisplayDimension)/float(aVisibleDimension);// We want to rasterize based on the largest scale used during the// transform animation, unless that would make us rasterize something// larger than the screen. But we never want to go smaller than the// minimum scale over the animation.if(FuzzyEqualsMultiplicative(displayVisibleRatio,aMaxScale,.01f)){// Using aMaxScale may make us rasterize something a fraction larger than// the screen. However, if aMaxScale happens to be the final scale of a// transform animation it is better to use aMaxScale so that for the// fraction of a second before we delayerize the composited texture it has// a better chance of being pixel aligned and composited without resampling// (avoiding visually clunky delayerization).returnaMaxScale;}returnstd::max(std::min(aMaxScale,displayVisibleRatio),aMinScale);}staticinlinevoidUpdateMinMaxScale(constnsIFrame*aFrame,constAnimationValue&aValue,gfxSize&aMinScale,gfxSize&aMaxScale){gfxSizesize=aValue.GetScaleValue(aFrame);aMaxScale.width=std::max<float>(aMaxScale.width,size.width);aMaxScale.height=std::max<float>(aMaxScale.height,size.height);aMinScale.width=std::min<float>(aMinScale.width,size.width);aMinScale.height=std::min<float>(aMinScale.height,size.height);}staticvoidGetMinAndMaxScaleForAnimationProperty(constnsIFrame*aFrame,nsTArray<RefPtr<dom::Animation>>&aAnimations,gfxSize&aMaxScale,gfxSize&aMinScale){for(dom::Animation*anim:aAnimations){// This method is only expected to be passed animations that are running on// the compositor and we only pass playing animations to the compositor,// which are, by definition, "relevant" animations (animations that are// not yet finished or which are filling forwards).MOZ_ASSERT(anim->IsRelevant());dom::KeyframeEffectReadOnly*effect=anim->GetEffect()?anim->GetEffect()->AsKeyframeEffect():nullptr;MOZ_ASSERT(effect,"A playing animation should have a keyframe effect");for(size_tpropIdx=effect->Properties().Length();propIdx--!=0;){constAnimationProperty&prop=effect->Properties()[propIdx];if(prop.mProperty!=eCSSProperty_transform){continue;}// We need to factor in the scale of the base style if the base style// will be used on the compositor.AnimationValuebaseStyle=effect->BaseStyle(prop.mProperty);if(!baseStyle.IsNull()){UpdateMinMaxScale(aFrame,baseStyle,aMinScale,aMaxScale);}for(constAnimationPropertySegment&segment:prop.mSegments){// In case of add or accumulate composite, StyleAnimationValue does// not have a valid value.if(segment.HasReplaceableFromValue()){UpdateMinMaxScale(aFrame,segment.mFromValue,aMinScale,aMaxScale);}if(segment.HasReplaceableToValue()){UpdateMinMaxScale(aFrame,segment.mToValue,aMinScale,aMaxScale);}}}}}gfxSizensLayoutUtils::ComputeSuitableScaleForAnimation(constnsIFrame*aFrame,constnsSize&aVisibleSize,constnsSize&aDisplaySize){gfxSizemaxScale(std::numeric_limits<gfxFloat>::min(),std::numeric_limits<gfxFloat>::min());gfxSizeminScale(std::numeric_limits<gfxFloat>::max(),std::numeric_limits<gfxFloat>::max());nsTArray<RefPtr<dom::Animation>>compositorAnimations=EffectCompositor::GetAnimationsForCompositor(aFrame,eCSSProperty_transform);GetMinAndMaxScaleForAnimationProperty(aFrame,compositorAnimations,maxScale,minScale);if(maxScale.width==std::numeric_limits<gfxFloat>::min()){// We didn't encounter a transformreturngfxSize(1.0,1.0);}returngfxSize(GetSuitableScale(maxScale.width,minScale.width,aVisibleSize.width,aDisplaySize.width),GetSuitableScale(maxScale.height,minScale.height,aVisibleSize.height,aDisplaySize.height));}boolnsLayoutUtils::AreAsyncAnimationsEnabled(){staticboolsAreAsyncAnimationsEnabled;staticboolsAsyncPrefCached=false;if(!sAsyncPrefCached){sAsyncPrefCached=true;Preferences::AddBoolVarCache(&sAreAsyncAnimationsEnabled,"layers.offmainthreadcomposition.async-animations");}returnsAreAsyncAnimationsEnabled&&gfxPlatform::OffMainThreadCompositingEnabled();}boolnsLayoutUtils::IsAnimationLoggingEnabled(){staticboolsShouldLog;staticboolsShouldLogPrefCached;if(!sShouldLogPrefCached){sShouldLogPrefCached=true;Preferences::AddBoolVarCache(&sShouldLog,"layers.offmainthreadcomposition.log-animations");}returnsShouldLog;}boolnsLayoutUtils::GPUImageScalingEnabled(){staticboolsGPUImageScalingEnabled;staticboolsGPUImageScalingPrefInitialised=false;if(!sGPUImageScalingPrefInitialised){sGPUImageScalingPrefInitialised=true;sGPUImageScalingEnabled=Preferences::GetBool("layout.gpu-image-scaling.enabled",false);}returnsGPUImageScalingEnabled;}boolnsLayoutUtils::AnimatedImageLayersEnabled(){staticboolsAnimatedImageLayersEnabled;staticboolsAnimatedImageLayersPrefCached=false;if(!sAnimatedImageLayersPrefCached){sAnimatedImageLayersPrefCached=true;Preferences::AddBoolVarCache(&sAnimatedImageLayersEnabled,"layout.animated-image-layers.enabled",false);}returnsAnimatedImageLayersEnabled;}boolnsLayoutUtils::CSSFiltersEnabled(){staticboolsCSSFiltersEnabled;staticboolsCSSFiltersPrefCached=false;if(!sCSSFiltersPrefCached){sCSSFiltersPrefCached=true;Preferences::AddBoolVarCache(&sCSSFiltersEnabled,"layout.css.filters.enabled",false);}returnsCSSFiltersEnabled;}boolnsLayoutUtils::CSSClipPathShapesEnabled(){staticboolsCSSClipPathShapesEnabled;staticboolsCSSClipPathShapesPrefCached=false;if(!sCSSClipPathShapesPrefCached){sCSSClipPathShapesPrefCached=true;Preferences::AddBoolVarCache(&sCSSClipPathShapesEnabled,"layout.css.clip-path-shapes.enabled",false);}returnsCSSClipPathShapesEnabled;}boolnsLayoutUtils::UnsetValueEnabled(){staticboolsUnsetValueEnabled;staticboolsUnsetValuePrefCached=false;if(!sUnsetValuePrefCached){sUnsetValuePrefCached=true;Preferences::AddBoolVarCache(&sUnsetValueEnabled,"layout.css.unset-value.enabled",false);}returnsUnsetValueEnabled;}boolnsLayoutUtils::IsGridTemplateSubgridValueEnabled(){staticboolsGridTemplateSubgridValueEnabled;staticboolsGridTemplateSubgridValueEnabledPrefCached=false;if(!sGridTemplateSubgridValueEnabledPrefCached){sGridTemplateSubgridValueEnabledPrefCached=true;Preferences::AddBoolVarCache(&sGridTemplateSubgridValueEnabled,GRID_TEMPLATE_SUBGRID_ENABLED_PREF_NAME,false);}returnsGridTemplateSubgridValueEnabled;}boolnsLayoutUtils::IsTextAlignUnsafeValueEnabled(){staticboolsTextAlignUnsafeValueEnabled;staticboolsTextAlignUnsafeValueEnabledPrefCached=false;if(!sTextAlignUnsafeValueEnabledPrefCached){sTextAlignUnsafeValueEnabledPrefCached=true;Preferences::AddBoolVarCache(&sTextAlignUnsafeValueEnabled,TEXT_ALIGN_UNSAFE_ENABLED_PREF_NAME,false);}returnsTextAlignUnsafeValueEnabled;}voidnsLayoutUtils::UnionChildOverflow(nsIFrame*aFrame,nsOverflowAreas&aOverflowAreas,FrameChildListIDsaSkipChildLists){// Iterate over all children except pop-ups.FrameChildListIDsskip=aSkipChildLists|nsIFrame::kSelectPopupList|nsIFrame::kPopupList;for(nsIFrame::ChildListIteratorchildLists(aFrame);!childLists.IsDone();childLists.Next()){if(skip.Contains(childLists.CurrentID())){continue;}nsFrameListchildren=childLists.CurrentList();for(nsFrameList::Enumeratore(children);!e.AtEnd();e.Next()){nsIFrame*child=e.get();nsOverflowAreaschildOverflow=child->GetOverflowAreas()+child->GetPosition();aOverflowAreas.UnionWith(childOverflow);}}}staticvoidDestroyViewID(void*aObject,nsIAtom*aPropertyName,void*aPropertyValue,void*aData){ViewID*id=static_cast<ViewID*>(aPropertyValue);GetContentMap().Remove(*id);deleteid;}/** * A namespace class for static layout utilities. */boolnsLayoutUtils::FindIDFor(constnsIContent*aContent,ViewID*aOutViewId){void*scrollIdProperty=aContent->GetProperty(nsGkAtoms::RemoteId);if(scrollIdProperty){*aOutViewId=*static_cast<ViewID*>(scrollIdProperty);returntrue;}returnfalse;}ViewIDnsLayoutUtils::FindOrCreateIDFor(nsIContent*aContent){ViewIDscrollId;if(!FindIDFor(aContent,&scrollId)){scrollId=sScrollIdCounter++;aContent->SetProperty(nsGkAtoms::RemoteId,newViewID(scrollId),DestroyViewID);GetContentMap().Put(scrollId,aContent);}returnscrollId;}nsIContent*nsLayoutUtils::FindContentFor(ViewIDaId){MOZ_ASSERT(aId!=FrameMetrics::NULL_SCROLL_ID,"Cannot find a content element in map for null IDs.");nsIContent*content;boolexists=GetContentMap().Get(aId,&content);if(exists){returncontent;}else{returnnullptr;}}nsIFrame*GetScrollFrameFromContent(nsIContent*aContent){nsIFrame*frame=aContent->GetPrimaryFrame();if(aContent->OwnerDoc()->GetRootElement()==aContent){nsIPresShell*presShell=frame?frame->PresContext()->PresShell():nullptr;if(!presShell){presShell=aContent->OwnerDoc()->GetShell();}// We want the scroll frame, the root scroll frame differs from all// others in that the primary frame is not the scroll frame.nsIFrame*rootScrollFrame=presShell?presShell->GetRootScrollFrame():nullptr;if(rootScrollFrame){frame=rootScrollFrame;}}returnframe;}nsIScrollableFrame*nsLayoutUtils::FindScrollableFrameFor(ViewIDaId){nsIContent*content=FindContentFor(aId);if(!content){returnnullptr;}nsIFrame*scrollFrame=GetScrollFrameFromContent(content);returnscrollFrame?scrollFrame->GetScrollTargetFrame():nullptr;}ViewIDnsLayoutUtils::FindIDForScrollableFrame(nsIScrollableFrame*aScrollable){if(!aScrollable){returnFrameMetrics::NULL_SCROLL_ID;}nsIFrame*scrollFrame=do_QueryFrame(aScrollable);nsIContent*scrollContent=scrollFrame->GetContent();FrameMetrics::ViewIDscrollId;if(scrollContent&&nsLayoutUtils::FindIDFor(scrollContent,&scrollId)){returnscrollId;}returnFrameMetrics::NULL_SCROLL_ID;}staticnsRectApplyRectMultiplier(nsRectaRect,floataMultiplier){if(aMultiplier==1.0f){returnaRect;}floatnewWidth=aRect.width*aMultiplier;floatnewHeight=aRect.height*aMultiplier;floatnewX=aRect.x-((newWidth-aRect.width)/2.0f);floatnewY=aRect.y-((newHeight-aRect.height)/2.0f);// Rounding doesn't matter too much here, do a round-inreturnnsRect(ceil(newX),ceil(newY),floor(newWidth),floor(newHeight));}boolnsLayoutUtils::UsesAsyncScrolling(nsIFrame*aFrame){#ifdef MOZ_WIDGET_ANDROID// We always have async scrolling for androidreturntrue;#endifreturnAsyncPanZoomEnabled(aFrame);}boolnsLayoutUtils::AsyncPanZoomEnabled(nsIFrame*aFrame){// We use this as a shortcut, since if the compositor will never use APZ,// no widget will either.if(!gfxPlatform::AsyncPanZoomEnabled()){returnfalse;}nsIFrame*frame=nsLayoutUtils::GetDisplayRootFrame(aFrame);nsIWidget*widget=frame->GetNearestWidget();if(!widget){returnfalse;}returnwidget->AsyncPanZoomEnabled();}floatnsLayoutUtils::GetCurrentAPZResolutionScale(nsIPresShell*aShell){returnaShell?aShell->GetCumulativeNonRootScaleResolution():1.0;}// Return the maximum displayport size, based on the LayerManager's maximum// supported texture size. The result is in app units.staticnscoordGetMaxDisplayPortSize(nsIContent*aContent){MOZ_ASSERT(!gfxPrefs::LayersTilesEnabled(),"Do not clamp displayports if tiling is enabled");nsIFrame*frame=aContent->GetPrimaryFrame();if(!frame){returnnscoord_MAX;}frame=nsLayoutUtils::GetDisplayRootFrame(frame);nsIWidget*widget=frame->GetNearestWidget();if(!widget){returnnscoord_MAX;}LayerManager*lm=widget->GetLayerManager();if(!lm){returnnscoord_MAX;}nsPresContext*presContext=frame->PresContext();int32_tmaxSizeInDevPixels=lm->GetMaxTextureSize();if(maxSizeInDevPixels<0||maxSizeInDevPixels==INT_MAX){returnnscoord_MAX;}returnpresContext->DevPixelsToAppUnits(maxSizeInDevPixels);}staticnsRectGetDisplayPortFromRectData(nsIContent*aContent,DisplayPortPropertyData*aRectData,floataMultiplier){// In the case where the displayport is set as a rect, we assume it is// already aligned and clamped as necessary. The burden to do that is// on the setter of the displayport. In practice very few places set the// displayport directly as a rect (mostly tests). We still do need to// expand it by the multiplier though.returnApplyRectMultiplier(aRectData->mRect,aMultiplier);}staticnsRectGetDisplayPortFromMarginsData(nsIContent*aContent,DisplayPortMarginsPropertyData*aMarginsData,floataMultiplier){// In the case where the displayport is set via margins, we apply the margins// to a base rect. Then we align the expanded rect based on the alignment// requested, further expand the rect by the multiplier, and finally, clamp it// to the size of the scrollable rect.nsRectbase;if(nsRect*baseData=static_cast<nsRect*>(aContent->GetProperty(nsGkAtoms::DisplayPortBase))){base=*baseData;}else{// In theory we shouldn't get here, but we do sometimes (see bug 1212136).// Fall through for graceful handling.}nsIFrame*frame=GetScrollFrameFromContent(aContent);if(!frame){// Turns out we can't really compute it. Oops. We still should return// something sane. Note that since we can't clamp the rect without a// frame, we don't apply the multiplier either as it can cause the result// to leak outside the scrollable area.NS_WARNING("Attempting to get a displayport from a content with no primary frame!");returnbase;}boolisRoot=false;if(aContent->OwnerDoc()->GetRootElement()==aContent){isRoot=true;}nsPointscrollPos;if(nsIScrollableFrame*scrollableFrame=frame->GetScrollTargetFrame()){scrollPos=scrollableFrame->GetScrollPosition();}nsPresContext*presContext=frame->PresContext();int32_tauPerDevPixel=presContext->AppUnitsPerDevPixel();LayoutDeviceToScreenScale2Dres(presContext->PresShell()->GetCumulativeResolution()*nsLayoutUtils::GetTransformToAncestorScale(frame));// Calculate the expanded scrollable rect, which we'll be clamping the// displayport to.nsRectexpandedScrollableRect=nsLayoutUtils::CalculateExpandedScrollableRect(frame);// GetTransformToAncestorScale() can return 0. In this case, just return the// base rect (clamped to the expanded scrollable rect), as other calculations// would run into divisions by zero.if(res==LayoutDeviceToScreenScale2D(0,0)){// Make sure the displayport remains within the scrollable rect.returnbase.MoveInsideAndClamp(expandedScrollableRect-scrollPos);}// First convert the base rect to screen pixelsLayoutDeviceToScreenScale2DparentRes=res;if(isRoot){// the base rect for root scroll frames is specified in the parent document// coordinate space, so it doesn't include the local resolution.floatlocalRes=presContext->PresShell()->GetResolution();parentRes.xScale/=localRes;parentRes.yScale/=localRes;}ScreenRectscreenRect=LayoutDeviceRect::FromAppUnits(base,auPerDevPixel)*parentRes;// Note on the correctness of applying the alignment in Screen space:// The correct space to apply the alignment in would be Layer space, but// we don't necessarily know the scale to convert to Layer space at this// point because Layout may not yet have chosen the resolution at which to// render (it chooses that in FrameLayerBuilder, but this can be called// during display list building). Therefore, we perform the alignment in// Screen space, which basically assumes that Layout chose to render at// screen resolution; since this is what Layout does most of the time,// this is a good approximation. A proper solution would involve moving// the choosing of the resolution to display-list building time.ScreenSizealignment;if(APZCCallbackHelper::IsDisplayportSuppressed()){alignment=ScreenSize(1,1);}elseif(gfxPrefs::LayersTilesEnabled()){// Don't align to tiles if they are too large, because we could expand// the displayport by a lot which can take more paint time. It's a tradeoff// though because if we don't align to tiles we have more waste on upload.IntSizetileSize=gfxVars::TileSize();alignment=ScreenSize(std::min(256,tileSize.width),std::min(256,tileSize.height));}else{// If we're not drawing with tiles then we need to be careful about not// hitting the max texture size and we only need 1 draw call per layer// so we can align to a smaller multiple.alignment=ScreenSize(128,128);}// Avoid division by zero.if(alignment.width==0){alignment.width=128;}if(alignment.height==0){alignment.height=128;}if(gfxPrefs::LayersTilesEnabled()){// Expand the rect by the marginsscreenRect.Inflate(aMarginsData->mMargins);}else{// Calculate the displayport to make sure we fit within the max texture size// when not tiling.nscoordmaxSizeAppUnits=GetMaxDisplayPortSize(aContent);if(maxSizeAppUnits==nscoord_MAX){// Pick a safe maximum displayport size for sanity purposes. This is the// lowest maximum texture size on tileless-platforms (Windows, D3D10).maxSizeAppUnits=presContext->DevPixelsToAppUnits(8192);}// The alignment code can round up to 3 tiles, we want to make sure// that the displayport can grow by up to 3 tiles without going// over the max texture size.constintMAX_ALIGN_ROUNDING=3;// Find the maximum size in screen pixels.int32_tmaxSizeDevPx=presContext->AppUnitsToDevPixels(maxSizeAppUnits);int32_tmaxWidthScreenPx=floor(double(maxSizeDevPx)*res.xScale)-MAX_ALIGN_ROUNDING*alignment.width;int32_tmaxHeightScreenPx=floor(double(maxSizeDevPx)*res.yScale)-MAX_ALIGN_ROUNDING*alignment.height;// For each axis, inflate the margins up to the maximum size.constScreenMargin&margins=aMarginsData->mMargins;if(screenRect.height<maxHeightScreenPx){int32_tbudget=maxHeightScreenPx-screenRect.height;// Scale the margins down to fit into the budget if necessary, maintaining// their relative ratio.floatscale=std::min(1.0f,float(budget)/margins.TopBottom());floattop=margins.top*scale;floatbottom=margins.bottom*scale;screenRect.y-=top;screenRect.height+=top+bottom;}if(screenRect.width<maxWidthScreenPx){int32_tbudget=maxWidthScreenPx-screenRect.width;floatscale=std::min(1.0f,float(budget)/margins.LeftRight());floatleft=margins.left*scale;floatright=margins.right*scale;screenRect.x-=left;screenRect.width+=left+right;}}ScreenPointscrollPosScreen=LayoutDevicePoint::FromAppUnits(scrollPos,auPerDevPixel)*res;// Round-out the display port to the nearest alignment (tiles)screenRect+=scrollPosScreen;floatx=alignment.width*floor(screenRect.x/alignment.width);floaty=alignment.height*floor(screenRect.y/alignment.height);floatw=alignment.width*ceil(screenRect.width/alignment.width+1);floath=alignment.height*ceil(screenRect.height/alignment.height+1);screenRect=ScreenRect(x,y,w,h);screenRect-=scrollPosScreen;// Convert the aligned rect back into app units.nsRectresult=LayoutDeviceRect::ToAppUnits(screenRect/res,auPerDevPixel);// If we have non-zero margins, expand the displayport for the low-res buffer// if that's what we're drawing. If we have zero margins, we want the// displayport to reflect the scrollport.if(aMarginsData->mMargins!=ScreenMargin()){result=ApplyRectMultiplier(result,aMultiplier);}// Make sure the displayport remains within the scrollable rect.result=result.MoveInsideAndClamp(expandedScrollableRect-scrollPos);returnresult;}staticboolHasVisibleAnonymousContents(nsIDocument*aDoc){for(RefPtr<AnonymousContent>&ac:aDoc->GetAnonymousContents()){Element*elem=ac->GetContentNode();// We check to see if the anonymous content node has a frame. If it doesn't,// that means that's not visible to the user because e.g. it's display:none.// For now we assume that if it has a frame, it is visible. We might be able// to refine this further by adding complexity if it turns out this condition// results in a lot of false positives.if(elem&&elem->GetPrimaryFrame()){returntrue;}}returnfalse;}boolnsLayoutUtils::ShouldDisableApzForElement(nsIContent*aContent){if(!aContent){returnfalse;}nsIDocument*doc=aContent->GetComposedDoc();nsIPresShell*rootShell=APZCCallbackHelper::GetRootContentDocumentPresShellForContent(aContent);if(rootShell){if(nsIDocument*rootDoc=rootShell->GetDocument()){nsIContent*rootContent=rootShell->GetRootScrollFrame()?rootShell->GetRootScrollFrame()->GetContent():rootDoc->GetDocumentElement();// For the AccessibleCaret: disable APZ on any scrollable subframes that// are not the root scrollframe of a document, if the document has any// visible anonymous contents.// If we find this is triggering in too many scenarios then we might// want to tighten this check further. The main use cases for which we want// to disable APZ as of this writing are listed in bug 1316318.if(aContent!=rootContent&&HasVisibleAnonymousContents(rootDoc)){returntrue;}}}if(!doc){returnfalse;}returngfxPrefs::APZDisableForScrollLinkedEffects()&&doc->HasScrollLinkedEffect();}staticboolGetDisplayPortData(nsIContent*aContent,DisplayPortPropertyData**aOutRectData,DisplayPortMarginsPropertyData**aOutMarginsData){MOZ_ASSERT(aOutRectData&&aOutMarginsData);*aOutRectData=static_cast<DisplayPortPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPort));*aOutMarginsData=static_cast<DisplayPortMarginsPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPortMargins));if(!*aOutRectData&&!*aOutMarginsData){// This content element has no displayport data at allreturnfalse;}if(*aOutRectData&&*aOutMarginsData){// choose margins if equal priorityif((*aOutRectData)->mPriority>(*aOutMarginsData)->mPriority){*aOutMarginsData=nullptr;}else{*aOutRectData=nullptr;}}NS_ASSERTION((*aOutRectData==nullptr)!=(*aOutMarginsData==nullptr),"Only one of aOutRectData or aOutMarginsData should be set!");returntrue;}boolnsLayoutUtils::IsMissingDisplayPortBaseRect(nsIContent*aContent){DisplayPortPropertyData*rectData=nullptr;DisplayPortMarginsPropertyData*marginsData=nullptr;if(GetDisplayPortData(aContent,&rectData,&marginsData)&&marginsData){return!aContent->GetProperty(nsGkAtoms::DisplayPortBase);}returnfalse;}staticboolGetDisplayPortImpl(nsIContent*aContent,nsRect*aResult,floataMultiplier){DisplayPortPropertyData*rectData=nullptr;DisplayPortMarginsPropertyData*marginsData=nullptr;if(!GetDisplayPortData(aContent,&rectData,&marginsData)){returnfalse;}if(!aResult){// We have displayport data, but the caller doesn't want the actual// rect, so we don't need to actually compute it.returntrue;}nsRectresult;if(rectData){result=GetDisplayPortFromRectData(aContent,rectData,aMultiplier);}elseif(APZCCallbackHelper::IsDisplayportSuppressed()||nsLayoutUtils::ShouldDisableApzForElement(aContent)){DisplayPortMarginsPropertyDatanoMargins(ScreenMargin(),1);result=GetDisplayPortFromMarginsData(aContent,&noMargins,aMultiplier);}else{result=GetDisplayPortFromMarginsData(aContent,marginsData,aMultiplier);}if(!gfxPrefs::LayersTilesEnabled()){// Either we should have gotten a valid rect directly from the displayport// base, or we should have computed a valid rect from the margins.NS_ASSERTION(result.width<=GetMaxDisplayPortSize(aContent),"Displayport must be a valid texture size");NS_ASSERTION(result.height<=GetMaxDisplayPortSize(aContent),"Displayport must be a valid texture size");}*aResult=result;returntrue;}voidTranslateFromScrollPortToScrollFrame(nsIContent*aContent,nsRect*aRect){MOZ_ASSERT(aRect);nsIFrame*frame=GetScrollFrameFromContent(aContent);nsIScrollableFrame*scrollableFrame=frame?frame->GetScrollTargetFrame():nullptr;if(scrollableFrame){*aRect+=scrollableFrame->GetScrollPortRect().TopLeft();}}boolnsLayoutUtils::GetDisplayPort(nsIContent*aContent,nsRect*aResult,RelativeToaRelativeTo/* = RelativeTo::ScrollPort */){floatmultiplier=gfxPrefs::UseLowPrecisionBuffer()?1.0f/gfxPrefs::LowPrecisionResolution():1.0f;boolusingDisplayPort=GetDisplayPortImpl(aContent,aResult,multiplier);if(aResult&&usingDisplayPort&&aRelativeTo==RelativeTo::ScrollFrame){TranslateFromScrollPortToScrollFrame(aContent,aResult);}returnusingDisplayPort;}boolnsLayoutUtils::HasDisplayPort(nsIContent*aContent){returnGetDisplayPort(aContent,nullptr);}/* static */boolnsLayoutUtils::GetDisplayPortForVisibilityTesting(nsIContent*aContent,nsRect*aResult,RelativeToaRelativeTo/* = RelativeTo::ScrollPort */){MOZ_ASSERT(aResult);boolusingDisplayPort=GetDisplayPortImpl(aContent,aResult,1.0f);if(usingDisplayPort&&aRelativeTo==RelativeTo::ScrollFrame){TranslateFromScrollPortToScrollFrame(aContent,aResult);}returnusingDisplayPort;}boolnsLayoutUtils::SetDisplayPortMargins(nsIContent*aContent,nsIPresShell*aPresShell,constScreenMargin&aMargins,uint32_taPriority,RepaintModeaRepaintMode){MOZ_ASSERT(aContent);MOZ_ASSERT(aContent->GetComposedDoc()==aPresShell->GetDocument());DisplayPortMarginsPropertyData*currentData=static_cast<DisplayPortMarginsPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPortMargins));if(currentData&¤tData->mPriority>aPriority){returnfalse;}nsRectoldDisplayPort;boolhadDisplayPort=GetHighResolutionDisplayPort(aContent,&oldDisplayPort);aContent->SetProperty(nsGkAtoms::DisplayPortMargins,newDisplayPortMarginsPropertyData(aMargins,aPriority),nsINode::DeleteProperty<DisplayPortMarginsPropertyData>);nsRectnewDisplayPort;DebugOnly<bool>hasDisplayPort=GetHighResolutionDisplayPort(aContent,&newDisplayPort);MOZ_ASSERT(hasDisplayPort);boolchanged=!hadDisplayPort||!oldDisplayPort.IsEqualEdges(newDisplayPort);if(gfxPrefs::LayoutUseContainersForRootFrames()){nsIFrame*rootScrollFrame=aPresShell->GetRootScrollFrame();if(rootScrollFrame&&aContent==rootScrollFrame->GetContent()&&nsLayoutUtils::UsesAsyncScrolling(rootScrollFrame)){// We are setting a root displayport for a document.// If we have APZ, then set a special flag on the pres shell so// that we don't get scrollbars drawn.aPresShell->SetIgnoreViewportScrolling(true);}}if(changed&&aRepaintMode==RepaintMode::Repaint){nsIFrame*frame=aContent->GetPrimaryFrame();if(frame){frame->SchedulePaint();}}nsIFrame*frame=GetScrollFrameFromContent(aContent);nsIScrollableFrame*scrollableFrame=frame?frame->GetScrollTargetFrame():nullptr;if(!scrollableFrame){returntrue;}scrollableFrame->TriggerDisplayPortExpiration();// Display port margins changing means that the set of visible frames may// have drastically changed. Check if we should schedule an update.hadDisplayPort=scrollableFrame->GetDisplayPortAtLastApproximateFrameVisibilityUpdate(&oldDisplayPort);boolneedVisibilityUpdate=!hadDisplayPort;// Check if the total size has changed by a large factor.if(!needVisibilityUpdate){if((newDisplayPort.width>2*oldDisplayPort.width)||(oldDisplayPort.width>2*newDisplayPort.width)||(newDisplayPort.height>2*oldDisplayPort.height)||(oldDisplayPort.height>2*newDisplayPort.height)){needVisibilityUpdate=true;}}// Check if it's moved by a significant amount.if(!needVisibilityUpdate){if(nsRect*baseData=static_cast<nsRect*>(aContent->GetProperty(nsGkAtoms::DisplayPortBase))){nsRectbase=*baseData;if((std::abs(newDisplayPort.X()-oldDisplayPort.X())>base.width)||(std::abs(newDisplayPort.XMost()-oldDisplayPort.XMost())>base.width)||(std::abs(newDisplayPort.Y()-oldDisplayPort.Y())>base.height)||(std::abs(newDisplayPort.YMost()-oldDisplayPort.YMost())>base.height)){needVisibilityUpdate=true;}}}if(needVisibilityUpdate){aPresShell->ScheduleApproximateFrameVisibilityUpdateNow();}returntrue;}voidnsLayoutUtils::SetDisplayPortBase(nsIContent*aContent,constnsRect&aBase){aContent->SetProperty(nsGkAtoms::DisplayPortBase,newnsRect(aBase),nsINode::DeleteProperty<nsRect>);}voidnsLayoutUtils::SetDisplayPortBaseIfNotSet(nsIContent*aContent,constnsRect&aBase){if(!aContent->GetProperty(nsGkAtoms::DisplayPortBase)){SetDisplayPortBase(aContent,aBase);}}boolnsLayoutUtils::GetCriticalDisplayPort(nsIContent*aContent,nsRect*aResult){if(gfxPrefs::UseLowPrecisionBuffer()){returnGetDisplayPortImpl(aContent,aResult,1.0f);}returnfalse;}boolnsLayoutUtils::HasCriticalDisplayPort(nsIContent*aContent){returnGetCriticalDisplayPort(aContent,nullptr);}boolnsLayoutUtils::GetHighResolutionDisplayPort(nsIContent*aContent,nsRect*aResult){if(gfxPrefs::UseLowPrecisionBuffer()){returnGetCriticalDisplayPort(aContent,aResult);}returnGetDisplayPort(aContent,aResult);}voidnsLayoutUtils::RemoveDisplayPort(nsIContent*aContent){aContent->DeleteProperty(nsGkAtoms::DisplayPort);aContent->DeleteProperty(nsGkAtoms::DisplayPortMargins);}nsContainerFrame*nsLayoutUtils::LastContinuationWithChild(nsContainerFrame*aFrame){NS_PRECONDITION(aFrame,"NULL frame pointer");nsIFrame*f=aFrame->LastContinuation();while(!f->PrincipalChildList().FirstChild()&&f->GetPrevContinuation()){f=f->GetPrevContinuation();}returnstatic_cast<nsContainerFrame*>(f);}//staticFrameChildListIDnsLayoutUtils::GetChildListNameFor(nsIFrame*aChildFrame){nsIFrame::ChildListIDid=nsIFrame::kPrincipalList;if(aChildFrame->GetStateBits()&NS_FRAME_IS_OVERFLOW_CONTAINER){nsIFrame*pif=aChildFrame->GetPrevInFlow();if(pif->GetParent()==aChildFrame->GetParent()){id=nsIFrame::kExcessOverflowContainersList;}else{id=nsIFrame::kOverflowContainersList;}}// See if the frame is moved out of the flowelseif(aChildFrame->GetStateBits()&NS_FRAME_OUT_OF_FLOW){// Look at the style information to tellconstnsStyleDisplay*disp=aChildFrame->StyleDisplay();if(NS_STYLE_POSITION_ABSOLUTE==disp->mPosition){id=nsIFrame::kAbsoluteList;}elseif(NS_STYLE_POSITION_FIXED==disp->mPosition){if(nsLayoutUtils::IsReallyFixedPos(aChildFrame)){id=nsIFrame::kFixedList;}else{id=nsIFrame::kAbsoluteList;}#ifdef MOZ_XUL}elseif(StyleDisplay::MozPopup==disp->mDisplay){// Out-of-flows that are DISPLAY_POPUP must be kids of the root popup set#ifdef DEBUGnsIFrame*parent=aChildFrame->GetParent();NS_ASSERTION(parent&&parent->IsPopupSetFrame(),"Unexpected parent");#endif // DEBUGid=nsIFrame::kPopupList;#endif // MOZ_XUL}else{NS_ASSERTION(aChildFrame->IsFloating(),"not a floated frame");id=nsIFrame::kFloatList;}}else{LayoutFrameTypechildType=aChildFrame->Type();if(LayoutFrameType::MenuPopup==childType){nsIFrame*parent=aChildFrame->GetParent();MOZ_ASSERT(parent,"nsMenuPopupFrame can't be the root frame");if(parent){if(parent->IsPopupSetFrame()){id=nsIFrame::kPopupList;}else{nsIFrame*firstPopup=parent->GetChildList(nsIFrame::kPopupList).FirstChild();MOZ_ASSERT(!firstPopup||!firstPopup->GetNextSibling(),"We assume popupList only has one child, but it has more.");id=firstPopup==aChildFrame?nsIFrame::kPopupList:nsIFrame::kPrincipalList;}}else{id=nsIFrame::kPrincipalList;}}elseif(LayoutFrameType::TableColGroup==childType){id=nsIFrame::kColGroupList;}elseif(aChildFrame->IsTableCaption()){id=nsIFrame::kCaptionList;}else{id=nsIFrame::kPrincipalList;}}#ifdef DEBUG// Verify that the frame is actually in that child list or in the// corresponding overflow list.nsContainerFrame*parent=aChildFrame->GetParent();boolfound=parent->GetChildList(id).ContainsFrame(aChildFrame);if(!found){if(!(aChildFrame->GetStateBits()&NS_FRAME_OUT_OF_FLOW)){found=parent->GetChildList(nsIFrame::kOverflowList).ContainsFrame(aChildFrame);}elseif(aChildFrame->IsFloating()){found=parent->GetChildList(nsIFrame::kOverflowOutOfFlowList).ContainsFrame(aChildFrame);if(!found){found=parent->GetChildList(nsIFrame::kPushedFloatsList).ContainsFrame(aChildFrame);}}// else it's positioned and should have been on the 'id' child list.NS_POSTCONDITION(found,"not in child list");}#endifreturnid;}staticElement*GetPseudo(constnsIContent*aContent,nsIAtom*aPseudoProperty){MOZ_ASSERT(aPseudoProperty==nsGkAtoms::beforePseudoProperty||aPseudoProperty==nsGkAtoms::afterPseudoProperty);if(!aContent->MayHaveAnonymousChildren()){returnnullptr;}returnstatic_cast<Element*>(aContent->GetProperty(aPseudoProperty));}/*static*/Element*nsLayoutUtils::GetBeforePseudo(constnsIContent*aContent){returnGetPseudo(aContent,nsGkAtoms::beforePseudoProperty);}/*static*/nsIFrame*nsLayoutUtils::GetBeforeFrame(constnsIContent*aContent){Element*pseudo=GetBeforePseudo(aContent);returnpseudo?pseudo->GetPrimaryFrame():nullptr;}/*static*/Element*nsLayoutUtils::GetAfterPseudo(constnsIContent*aContent){returnGetPseudo(aContent,nsGkAtoms::afterPseudoProperty);}/*static*/nsIFrame*nsLayoutUtils::GetAfterFrame(constnsIContent*aContent){Element*pseudo=GetAfterPseudo(aContent);returnpseudo?pseudo->GetPrimaryFrame():nullptr;}// staticnsIFrame*nsLayoutUtils::GetClosestFrameOfType(nsIFrame*aFrame,LayoutFrameTypeaFrameType,nsIFrame*aStopAt){for(nsIFrame*frame=aFrame;frame;frame=frame->GetParent()){if(frame->Type()==aFrameType){returnframe;}if(frame==aStopAt){break;}}returnnullptr;}/* static */nsIFrame*nsLayoutUtils::GetPageFrame(nsIFrame*aFrame){returnGetClosestFrameOfType(aFrame,LayoutFrameType::Page);}// staticnsIFrame*nsLayoutUtils::GetStyleFrame(nsIFrame*aFrame){if(aFrame->IsTableWrapperFrame()){nsIFrame*inner=aFrame->PrincipalChildList().FirstChild();// inner may be null, if aFrame is mid-destructionreturninner;}returnaFrame;}nsIFrame*nsLayoutUtils::GetStyleFrame(constnsIContent*aContent){nsIFrame*frame=aContent->GetPrimaryFrame();if(!frame){returnnullptr;}returnnsLayoutUtils::GetStyleFrame(frame);}/* static */nsIFrame*nsLayoutUtils::GetRealPrimaryFrameFor(constnsIContent*aContent){nsIFrame*frame=aContent->GetPrimaryFrame();if(!frame){returnnullptr;}returnnsPlaceholderFrame::GetRealFrameFor(frame);}nsIFrame*nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame*aFrame){NS_ASSERTION(aFrame->IsPlaceholderFrame(),"Must have a placeholder here");if(aFrame->GetStateBits()&PLACEHOLDER_FOR_FLOAT){nsIFrame*outOfFlowFrame=nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);NS_ASSERTION(outOfFlowFrame->IsFloating(),"How did that happen?");returnoutOfFlowFrame;}returnnullptr;}// staticboolnsLayoutUtils::IsGeneratedContentFor(nsIContent*aContent,nsIFrame*aFrame,nsIAtom*aPseudoElement){NS_PRECONDITION(aFrame,"Must have a frame");NS_PRECONDITION(aPseudoElement,"Must have a pseudo name");if(!aFrame->IsGeneratedContentFrame()){returnfalse;}nsIFrame*parent=aFrame->GetParent();NS_ASSERTION(parent,"Generated content can't be root frame");if(parent->IsGeneratedContentFrame()){// Not the root of the generated contentreturnfalse;}if(aContent&&parent->GetContent()!=aContent){returnfalse;}return(aFrame->GetContent()->NodeInfo()->NameAtom()==nsGkAtoms::mozgeneratedcontentbefore)==(aPseudoElement==nsCSSPseudoElements::before);}// staticnsIFrame*nsLayoutUtils::GetCrossDocParentFrame(constnsIFrame*aFrame,nsPoint*aExtraOffset){nsIFrame*p=aFrame->GetParent();if(p)returnp;nsView*v=aFrame->GetView();if(!v)returnnullptr;v=v->GetParent();// anonymous inner viewif(!v)returnnullptr;if(aExtraOffset){*aExtraOffset+=v->GetPosition();}v=v->GetParent();// subdocumentframe's viewreturnv?v->GetFrame():nullptr;}// staticboolnsLayoutUtils::IsProperAncestorFrameCrossDoc(nsIFrame*aAncestorFrame,nsIFrame*aFrame,nsIFrame*aCommonAncestor){if(aFrame==aAncestorFrame)returnfalse;returnIsAncestorFrameCrossDoc(aAncestorFrame,aFrame,aCommonAncestor);}// staticboolnsLayoutUtils::IsAncestorFrameCrossDoc(constnsIFrame*aAncestorFrame,constnsIFrame*aFrame,constnsIFrame*aCommonAncestor){for(constnsIFrame*f=aFrame;f!=aCommonAncestor;f=GetCrossDocParentFrame(f)){if(f==aAncestorFrame)returntrue;}returnaCommonAncestor==aAncestorFrame;}// staticboolnsLayoutUtils::IsProperAncestorFrame(nsIFrame*aAncestorFrame,nsIFrame*aFrame,nsIFrame*aCommonAncestor){if(aFrame==aAncestorFrame)returnfalse;for(nsIFrame*f=aFrame;f!=aCommonAncestor;f=f->GetParent()){if(f==aAncestorFrame)returntrue;}returnaCommonAncestor==aAncestorFrame;}// staticint32_tnsLayoutUtils::DoCompareTreePosition(nsIContent*aContent1,nsIContent*aContent2,int32_taIf1Ancestor,int32_taIf2Ancestor,constnsIContent*aCommonAncestor){NS_PRECONDITION(aContent1,"aContent1 must not be null");NS_PRECONDITION(aContent2,"aContent2 must not be null");AutoTArray<nsINode*,32>content1Ancestors;nsINode*c1;for(c1=aContent1;c1&&c1!=aCommonAncestor;c1=c1->GetParentNode()){content1Ancestors.AppendElement(c1);}if(!c1&&aCommonAncestor){// So, it turns out aCommonAncestor was not an ancestor of c1. Oops.// Never mind. We can continue as if aCommonAncestor was null.aCommonAncestor=nullptr;}AutoTArray<nsINode*,32>content2Ancestors;nsINode*c2;for(c2=aContent2;c2&&c2!=aCommonAncestor;c2=c2->GetParentNode()){content2Ancestors.AppendElement(c2);}if(!c2&&aCommonAncestor){// So, it turns out aCommonAncestor was not an ancestor of c2.// We need to retry with no common ancestor hint.returnDoCompareTreePosition(aContent1,aContent2,aIf1Ancestor,aIf2Ancestor,nullptr);}intlast1=content1Ancestors.Length()-1;intlast2=content2Ancestors.Length()-1;nsINode*content1Ancestor=nullptr;nsINode*content2Ancestor=nullptr;while(last1>=0&&last2>=0&&((content1Ancestor=content1Ancestors.ElementAt(last1))==(content2Ancestor=content2Ancestors.ElementAt(last2)))){last1--;last2--;}if(last1<0){if(last2<0){NS_ASSERTION(aContent1==aContent2,"internal error?");return0;}// aContent1 is an ancestor of aContent2returnaIf1Ancestor;}if(last2<0){// aContent2 is an ancestor of aContent1returnaIf2Ancestor;}// content1Ancestor != content2Ancestor, so they must be siblings with the same parentnsINode*parent=content1Ancestor->GetParentNode();#ifdef DEBUG// TODO: remove the uglyness, see bug 598468.NS_ASSERTION(gPreventAssertInCompareTreePosition||parent,"no common ancestor at all???");#endif // DEBUGif(!parent){// different documents??return0;}int32_tindex1=parent->IndexOf(content1Ancestor);int32_tindex2=parent->IndexOf(content2Ancestor);if(index1<0||index2<0){// one of them must be anonymous; we can't determine the orderreturn0;}returnindex1-index2;}// staticnsIFrame*nsLayoutUtils::FillAncestors(nsIFrame*aFrame,nsIFrame*aStopAtAncestor,nsTArray<nsIFrame*>*aAncestors){while(aFrame&&aFrame!=aStopAtAncestor){aAncestors->AppendElement(aFrame);aFrame=nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);}returnaFrame;}// Return true if aFrame1 is after aFrame2staticboolIsFrameAfter(nsIFrame*aFrame1,nsIFrame*aFrame2){nsIFrame*f=aFrame2;do{f=f->GetNextSibling();if(f==aFrame1)returntrue;}while(f);returnfalse;}// staticint32_tnsLayoutUtils::DoCompareTreePosition(nsIFrame*aFrame1,nsIFrame*aFrame2,int32_taIf1Ancestor,int32_taIf2Ancestor,nsIFrame*aCommonAncestor){NS_PRECONDITION(aFrame1,"aFrame1 must not be null");NS_PRECONDITION(aFrame2,"aFrame2 must not be null");AutoTArray<nsIFrame*,20>frame2Ancestors;nsIFrame*nonCommonAncestor=FillAncestors(aFrame2,aCommonAncestor,&frame2Ancestors);returnDoCompareTreePosition(aFrame1,aFrame2,frame2Ancestors,aIf1Ancestor,aIf2Ancestor,nonCommonAncestor?aCommonAncestor:nullptr);}// staticint32_tnsLayoutUtils::DoCompareTreePosition(nsIFrame*aFrame1,nsIFrame*aFrame2,nsTArray<nsIFrame*>&aFrame2Ancestors,int32_taIf1Ancestor,int32_taIf2Ancestor,nsIFrame*aCommonAncestor){NS_PRECONDITION(aFrame1,"aFrame1 must not be null");NS_PRECONDITION(aFrame2,"aFrame2 must not be null");nsPresContext*presContext=aFrame1->PresContext();if(presContext!=aFrame2->PresContext()){NS_ERROR("no common ancestor at all, different documents");return0;}AutoTArray<nsIFrame*,20>frame1Ancestors;if(aCommonAncestor&&!FillAncestors(aFrame1,aCommonAncestor,&frame1Ancestors)){// We reached the root of the frame tree ... if aCommonAncestor was set,// it is wrongreturnDoCompareTreePosition(aFrame1,aFrame2,aIf1Ancestor,aIf2Ancestor,nullptr);}int32_tlast1=int32_t(frame1Ancestors.Length())-1;int32_tlast2=int32_t(aFrame2Ancestors.Length())-1;while(last1>=0&&last2>=0&&frame1Ancestors[last1]==aFrame2Ancestors[last2]){last1--;last2--;}if(last1<0){if(last2<0){NS_ASSERTION(aFrame1==aFrame2,"internal error?");return0;}// aFrame1 is an ancestor of aFrame2returnaIf1Ancestor;}if(last2<0){// aFrame2 is an ancestor of aFrame1returnaIf2Ancestor;}nsIFrame*ancestor1=frame1Ancestors[last1];nsIFrame*ancestor2=aFrame2Ancestors[last2];// Now we should be able to walk sibling chains to find which one is firstif(IsFrameAfter(ancestor2,ancestor1))return-1;if(IsFrameAfter(ancestor1,ancestor2))return1;NS_WARNING("Frames were in different child lists???");return0;}// staticnsIFrame*nsLayoutUtils::GetLastSibling(nsIFrame*aFrame){if(!aFrame){returnnullptr;}nsIFrame*next;while((next=aFrame->GetNextSibling())!=nullptr){aFrame=next;}returnaFrame;}// staticnsView*nsLayoutUtils::FindSiblingViewFor(nsView*aParentView,nsIFrame*aFrame){nsIFrame*parentViewFrame=aParentView->GetFrame();nsIContent*parentViewContent=parentViewFrame?parentViewFrame->GetContent():nullptr;for(nsView*insertBefore=aParentView->GetFirstChild();insertBefore;insertBefore=insertBefore->GetNextSibling()){nsIFrame*f=insertBefore->GetFrame();if(!f){// this view could be some anonymous view attached to a meaningful parentfor(nsView*searchView=insertBefore->GetParent();searchView;searchView=searchView->GetParent()){f=searchView->GetFrame();if(f){break;}}NS_ASSERTION(f,"Can't find a frame anywhere!");}if(!f||!aFrame->GetContent()||!f->GetContent()||CompareTreePosition(aFrame->GetContent(),f->GetContent(),parentViewContent)>0){// aFrame's content is after f's content (or we just don't know),// so put our view before f's viewreturninsertBefore;}}returnnullptr;}//staticnsIScrollableFrame*nsLayoutUtils::GetScrollableFrameFor(constnsIFrame*aScrolledFrame){nsIFrame*frame=aScrolledFrame->GetParent();nsIScrollableFrame*sf=do_QueryFrame(frame);return(sf&&sf->GetScrolledFrame()==aScrolledFrame)?sf:nullptr;}/* static */voidnsLayoutUtils::SetFixedPositionLayerData(Layer*aLayer,constnsIFrame*aViewportFrame,constnsRect&aAnchorRect,constnsIFrame*aFixedPosFrame,nsPresContext*aPresContext,constContainerLayerParameters&aContainerParameters){// Find out the rect of the viewport frame relative to the reference frame.// This, in conjunction with the container scale, will correspond to the// coordinate-space of the built layer.floatfactor=aPresContext->AppUnitsPerDevPixel();RectanchorRect(NSAppUnitsToFloatPixels(aAnchorRect.x,factor)*aContainerParameters.mXScale,NSAppUnitsToFloatPixels(aAnchorRect.y,factor)*aContainerParameters.mYScale,NSAppUnitsToFloatPixels(aAnchorRect.width,factor)*aContainerParameters.mXScale,NSAppUnitsToFloatPixels(aAnchorRect.height,factor)*aContainerParameters.mYScale);// Need to transform anchorRect from the container layer's coordinate system// into aLayer's coordinate system.Matrixtransform2d;if(aLayer->GetTransform().Is2D(&transform2d)){transform2d.Invert();anchorRect=transform2d.TransformBounds(anchorRect);}else{NS_ERROR("3D transform found between fixedpos content and its viewport (should never happen)");anchorRect=Rect(0,0,0,0);}// Work out the anchor point for this fixed position layer. We assume that// any positioning set (left/top/right/bottom) indicates that the// corresponding side of its container should be the anchor point,// defaulting to top-left.LayerPointanchor(anchorRect.x,anchorRect.y);int32_tsides=eSideBitsNone;if(aFixedPosFrame!=aViewportFrame){constnsStylePosition*position=aFixedPosFrame->StylePosition();if(position->mOffset.GetRightUnit()!=eStyleUnit_Auto){sides|=eSideBitsRight;if(position->mOffset.GetLeftUnit()!=eStyleUnit_Auto){sides|=eSideBitsLeft;anchor.x=anchorRect.x+anchorRect.width/2.f;}else{anchor.x=anchorRect.XMost();}}elseif(position->mOffset.GetLeftUnit()!=eStyleUnit_Auto){sides|=eSideBitsLeft;}if(position->mOffset.GetBottomUnit()!=eStyleUnit_Auto){sides|=eSideBitsBottom;if(position->mOffset.GetTopUnit()!=eStyleUnit_Auto){sides|=eSideBitsTop;anchor.y=anchorRect.y+anchorRect.height/2.f;}else{anchor.y=anchorRect.YMost();}}elseif(position->mOffset.GetTopUnit()!=eStyleUnit_Auto){sides|=eSideBitsTop;}}ViewIDid=FrameMetrics::NULL_SCROLL_ID;if(nsIFrame*rootScrollFrame=aPresContext->PresShell()->GetRootScrollFrame()){if(nsIContent*content=rootScrollFrame->GetContent()){id=FindOrCreateIDFor(content);}}aLayer->SetFixedPositionData(id,anchor,sides);}boolnsLayoutUtils::ViewportHasDisplayPort(nsPresContext*aPresContext){nsIFrame*rootScrollFrame=aPresContext->PresShell()->GetRootScrollFrame();returnrootScrollFrame&&nsLayoutUtils::HasDisplayPort(rootScrollFrame->GetContent());}boolnsLayoutUtils::IsFixedPosFrameInDisplayPort(constnsIFrame*aFrame){// Fixed-pos frames are parented by the viewport frame or the page content frame.// We'll assume that printing/print preview don't have displayports for their// pages!nsIFrame*parent=aFrame->GetParent();if(!parent||parent->GetParent()||aFrame->StyleDisplay()->mPosition!=NS_STYLE_POSITION_FIXED){returnfalse;}returnViewportHasDisplayPort(aFrame->PresContext());}NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(ScrollbarThumbLayerized,bool)/* static */voidnsLayoutUtils::SetScrollbarThumbLayerization(nsIFrame*aThumbFrame,boolaLayerize){aThumbFrame->SetProperty(ScrollbarThumbLayerized(),aLayerize);}boolnsLayoutUtils::IsScrollbarThumbLayerized(nsIFrame*aThumbFrame){returnaThumbFrame->GetProperty(ScrollbarThumbLayerized());}// staticnsIScrollableFrame*nsLayoutUtils::GetNearestScrollableFrameForDirection(nsIFrame*aFrame,DirectionaDirection){NS_ASSERTION(aFrame,"GetNearestScrollableFrameForDirection expects a non-null frame");for(nsIFrame*f=aFrame;f;f=nsLayoutUtils::GetCrossDocParentFrame(f)){nsIScrollableFrame*scrollableFrame=do_QueryFrame(f);if(scrollableFrame){ScrollbarStylesss=scrollableFrame->GetScrollbarStyles();uint32_tdirections=scrollableFrame->GetPerceivedScrollingDirections();if(aDirection==eVertical?(ss.mVertical!=NS_STYLE_OVERFLOW_HIDDEN&&(directions&nsIScrollableFrame::VERTICAL)):(ss.mHorizontal!=NS_STYLE_OVERFLOW_HIDDEN&&(directions&nsIScrollableFrame::HORIZONTAL)))returnscrollableFrame;}}returnnullptr;}// staticnsIScrollableFrame*nsLayoutUtils::GetNearestScrollableFrame(nsIFrame*aFrame,uint32_taFlags){NS_ASSERTION(aFrame,"GetNearestScrollableFrame expects a non-null frame");for(nsIFrame*f=aFrame;f;f=(aFlags&SCROLLABLE_SAME_DOC)?f->GetParent():nsLayoutUtils::GetCrossDocParentFrame(f)){nsIScrollableFrame*scrollableFrame=do_QueryFrame(f);if(scrollableFrame){if(aFlags&SCROLLABLE_ONLY_ASYNC_SCROLLABLE){if(scrollableFrame->WantAsyncScroll()){returnscrollableFrame;}}else{ScrollbarStylesss=scrollableFrame->GetScrollbarStyles();if((aFlags&SCROLLABLE_INCLUDE_HIDDEN)||ss.mVertical!=NS_STYLE_OVERFLOW_HIDDEN||ss.mHorizontal!=NS_STYLE_OVERFLOW_HIDDEN){returnscrollableFrame;}}if(aFlags&SCROLLABLE_ALWAYS_MATCH_ROOT){nsIPresShell*ps=f->PresContext()->PresShell();if(ps->GetRootScrollFrame()==f&&ps->GetDocument()&&ps->GetDocument()->IsRootDisplayDocument()){returnscrollableFrame;}}}if((aFlags&SCROLLABLE_FIXEDPOS_FINDS_ROOT)&&f->StyleDisplay()->mPosition==NS_STYLE_POSITION_FIXED&&nsLayoutUtils::IsReallyFixedPos(f)){returnf->PresContext()->PresShell()->GetRootScrollFrameAsScrollable();}}returnnullptr;}// staticnsRectnsLayoutUtils::GetScrolledRect(nsIFrame*aScrolledFrame,constnsRect&aScrolledFrameOverflowArea,constnsSize&aScrollPortSize,uint8_taDirection){WritingModewm=aScrolledFrame->GetWritingMode();// Potentially override the frame's direction to use the direction found// by ScrollFrameHelper::GetScrolledFrameDir()wm.SetDirectionFromBidiLevel(aDirection==NS_STYLE_DIRECTION_RTL?1:0);nscoordx1=aScrolledFrameOverflowArea.x,x2=aScrolledFrameOverflowArea.XMost(),y1=aScrolledFrameOverflowArea.y,y2=aScrolledFrameOverflowArea.YMost();boolhorizontal=!wm.IsVertical();// Clamp the horizontal start-edge (x1 or x2, depending whether the logical// axis that corresponds to horizontal progresses from L-R or R-L).// In horizontal writing mode, we need to check IsInlineReversed() to see// which side to clamp; in vertical mode, it depends on the block direction.if((horizontal&&!wm.IsInlineReversed())||wm.IsVerticalLR()){if(x1<0){x1=0;}}else{if(x2>aScrollPortSize.width){x2=aScrollPortSize.width;}// When the scrolled frame chooses a size larger than its available width// (because its padding alone is larger than the available width), we need// to keep the start-edge of the scroll frame anchored to the start-edge of// the scrollport.// When the scrolled frame is RTL, this means moving it in our left-based// coordinate system, so we need to compensate for its extra width here by// effectively repositioning the frame.nscoordextraWidth=std::max(0,aScrolledFrame->GetSize().width-aScrollPortSize.width);x2+=extraWidth;}// Similarly, clamp the vertical start-edge.// In horizontal writing mode, the block direction is always top-to-bottom;// in vertical writing mode, we need to check IsInlineReversed().if(horizontal||!wm.IsInlineReversed()){if(y1<0){y1=0;}}else{if(y2>aScrollPortSize.height){y2=aScrollPortSize.height;}nscoordextraHeight=std::max(0,aScrolledFrame->GetSize().height-aScrollPortSize.height);y2+=extraHeight;}returnnsRect(x1,y1,x2-x1,y2-y1);}//staticboolnsLayoutUtils::HasPseudoStyle(nsIContent*aContent,nsStyleContext*aStyleContext,CSSPseudoElementTypeaPseudoElement,nsPresContext*aPresContext){NS_PRECONDITION(aPresContext,"Must have a prescontext");RefPtr<nsStyleContext>pseudoContext;if(aContent){pseudoContext=aPresContext->StyleSet()->ProbePseudoElementStyle(aContent->AsElement(),aPseudoElement,aStyleContext);}returnpseudoContext!=nullptr;}nsPointnsLayoutUtils::GetDOMEventCoordinatesRelativeTo(nsIDOMEvent*aDOMEvent,nsIFrame*aFrame){if(!aDOMEvent)returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);WidgetEvent*event=aDOMEvent->WidgetEventPtr();if(!event)returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);returnGetEventCoordinatesRelativeTo(event,aFrame);}nsPointnsLayoutUtils::GetEventCoordinatesRelativeTo(constWidgetEvent*aEvent,nsIFrame*aFrame){if(!aEvent||(aEvent->mClass!=eMouseEventClass&&aEvent->mClass!=eMouseScrollEventClass&&aEvent->mClass!=eWheelEventClass&&aEvent->mClass!=eDragEventClass&&aEvent->mClass!=eSimpleGestureEventClass&&aEvent->mClass!=ePointerEventClass&&aEvent->mClass!=eGestureNotifyEventClass&&aEvent->mClass!=eTouchEventClass&&aEvent->mClass!=eQueryContentEventClass))returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);returnGetEventCoordinatesRelativeTo(aEvent,aEvent->AsGUIEvent()->mRefPoint,aFrame);}nsPointnsLayoutUtils::GetEventCoordinatesRelativeTo(constWidgetEvent*aEvent,constLayoutDeviceIntPoint&aPoint,nsIFrame*aFrame){if(!aFrame){returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);}nsIWidget*widget=aEvent->AsGUIEvent()->mWidget;if(!widget){returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);}returnGetEventCoordinatesRelativeTo(widget,aPoint,aFrame);}nsPointnsLayoutUtils::GetEventCoordinatesRelativeTo(nsIWidget*aWidget,constLayoutDeviceIntPoint&aPoint,nsIFrame*aFrame){if(!aFrame||!aWidget){returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);}nsView*view=aFrame->GetView();if(view){nsIWidget*frameWidget=view->GetWidget();if(frameWidget&&frameWidget==aWidget){// Special case this cause it happens a lot.// This also fixes bug 664707, events in the extra-special case of select// dropdown popups that are transformed.nsPresContext*presContext=aFrame->PresContext();nsPointpt(presContext->DevPixelsToAppUnits(aPoint.x),presContext->DevPixelsToAppUnits(aPoint.y));pt=pt-view->ViewToWidgetOffset();pt=pt.RemoveResolution(GetCurrentAPZResolutionScale(presContext->PresShell()));returnpt;}}/* If we walk up the frame tree and discover that any of the frames are * transformed, we need to do extra work to convert from the global * space to the local space. */nsIFrame*rootFrame=aFrame;booltransformFound=false;for(nsIFrame*f=aFrame;f;f=GetCrossDocParentFrame(f)){if(f->IsTransformed()){transformFound=true;}rootFrame=f;}nsView*rootView=rootFrame->GetView();if(!rootView){returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);}nsPointwidgetToView=TranslateWidgetToView(rootFrame->PresContext(),aWidget,aPoint,rootView);if(widgetToView==nsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE)){returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);}// Convert from root document app units to app units of the document aFrame// is in.int32_trootAPD=rootFrame->PresContext()->AppUnitsPerDevPixel();int32_tlocalAPD=aFrame->PresContext()->AppUnitsPerDevPixel();widgetToView=widgetToView.ScaleToOtherAppUnits(rootAPD,localAPD);nsIPresShell*shell=aFrame->PresContext()->PresShell();// XXX Bug 1224748 - Update nsLayoutUtils functions to correctly handle nsPresShell resolutionwidgetToView=widgetToView.RemoveResolution(GetCurrentAPZResolutionScale(shell));/* If we encountered a transform, we can't do simple arithmetic to figure * out how to convert back to aFrame's coordinates and must use the CTM. */if(transformFound||nsSVGUtils::IsInSVGTextSubtree(aFrame)){returnTransformRootPointToFrame(aFrame,widgetToView);}/* Otherwise, all coordinate systems are translations of one another, * so we can just subtract out the difference. */returnwidgetToView-aFrame->GetOffsetToCrossDoc(rootFrame);}nsIFrame*nsLayoutUtils::GetPopupFrameForEventCoordinates(nsPresContext*aPresContext,constWidgetEvent*aEvent){#ifdef MOZ_XULnsXULPopupManager*pm=nsXULPopupManager::GetInstance();if(!pm){returnnullptr;}nsTArray<nsIFrame*>popups;pm->GetVisiblePopups(popups);uint32_ti;// Search from top to bottomfor(i=0;i<popups.Length();i++){nsIFrame*popup=popups[i];if(popup->PresContext()->GetRootPresContext()==aPresContext&&popup->GetScrollableOverflowRect().Contains(GetEventCoordinatesRelativeTo(aEvent,popup))){returnpopup;}}#endifreturnnullptr;}staticvoidConstrainToCoordValues(float&aStart,float&aSize){MOZ_ASSERT(aSize>=0);// Here we try to make sure that the resulting nsRect will continue to cover// as much of the area that was covered by the original gfx Rect as possible.// We clamp the bounds of the rect to {nscoord_MIN,nscoord_MAX} since// nsRect::X/Y() and nsRect::XMost/YMost() can't return values outwith this// range:floatend=aStart+aSize;aStart=clamped(aStart,float(nscoord_MIN),float(nscoord_MAX));end=clamped(end,float(nscoord_MIN),float(nscoord_MAX));aSize=end-aStart;// We must also clamp aSize to {0,nscoord_MAX} since nsRect::Width/Height()// can't return a value greater than nscoord_MAX. If aSize is greater than// nscoord_MAX then we reduce it to nscoord_MAX while keeping the rect// centered:if(aSize>nscoord_MAX){floatexcess=aSize-nscoord_MAX;excess/=2;aStart+=excess;aSize=nscoord_MAX;}}/** * Given a gfxFloat, constrains its value to be between nscoord_MIN and nscoord_MAX. * * @param aVal The value to constrain (in/out) */staticvoidConstrainToCoordValues(gfxFloat&aVal){if(aVal<=nscoord_MIN)aVal=nscoord_MIN;elseif(aVal>=nscoord_MAX)aVal=nscoord_MAX;}staticvoidConstrainToCoordValues(gfxFloat&aStart,gfxFloat&aSize){gfxFloatmax=aStart+aSize;// Clamp the end points to within nscoord rangeConstrainToCoordValues(aStart);ConstrainToCoordValues(max);aSize=max-aStart;// If the width if still greater than the max nscoord, then bring both// endpoints in by the same amount until it fits.if(aSize>nscoord_MAX){gfxFloatexcess=aSize-nscoord_MAX;excess/=2;aStart+=excess;aSize=nscoord_MAX;}elseif(aSize<nscoord_MIN){gfxFloatexcess=aSize-nscoord_MIN;excess/=2;aStart-=excess;aSize=nscoord_MIN;}}nsRectnsLayoutUtils::RoundGfxRectToAppRect(constRect&aRect,floataFactor){/* Get a new Rect whose units are app units by scaling by the specified factor. */RectscaledRect=aRect;scaledRect.ScaleRoundOut(aFactor);/* We now need to constrain our results to the max and min values for coords. */ConstrainToCoordValues(scaledRect.x,scaledRect.width);ConstrainToCoordValues(scaledRect.y,scaledRect.height);/* Now typecast everything back. This is guaranteed to be safe. */returnnsRect(nscoord(scaledRect.X()),nscoord(scaledRect.Y()),nscoord(scaledRect.Width()),nscoord(scaledRect.Height()));}nsRectnsLayoutUtils::RoundGfxRectToAppRect(constgfxRect&aRect,floataFactor){/* Get a new gfxRect whose units are app units by scaling by the specified factor. */gfxRectscaledRect=aRect;scaledRect.ScaleRoundOut(aFactor);/* We now need to constrain our results to the max and min values for coords. */ConstrainToCoordValues(scaledRect.x,scaledRect.width);ConstrainToCoordValues(scaledRect.y,scaledRect.height);/* Now typecast everything back. This is guaranteed to be safe. */returnnsRect(nscoord(scaledRect.X()),nscoord(scaledRect.Y()),nscoord(scaledRect.Width()),nscoord(scaledRect.Height()));}nsRegionnsLayoutUtils::RoundedRectIntersectRect(constnsRect&aRoundedRect,constnscoordaRadii[8],constnsRect&aContainedRect){// rectFullHeight and rectFullWidth together will approximately contain// the total area of the frame minus the rounded corners.nsRectrectFullHeight=aRoundedRect;nscoordxDiff=std::max(aRadii[eCornerTopLeftX],aRadii[eCornerBottomLeftX]);rectFullHeight.x+=xDiff;rectFullHeight.width-=std::max(aRadii[eCornerTopRightX],aRadii[eCornerBottomRightX])+xDiff;nsRectr1;r1.IntersectRect(rectFullHeight,aContainedRect);nsRectrectFullWidth=aRoundedRect;nscoordyDiff=std::max(aRadii[eCornerTopLeftY],aRadii[eCornerTopRightY]);rectFullWidth.y+=yDiff;rectFullWidth.height-=std::max(aRadii[eCornerBottomLeftY],aRadii[eCornerBottomRightY])+yDiff;nsRectr2;r2.IntersectRect(rectFullWidth,aContainedRect);nsRegionresult;result.Or(r1,r2);returnresult;}nsIntRegionnsLayoutUtils::RoundedRectIntersectIntRect(constnsIntRect&aRoundedRect,constRectCornerRadii&aCornerRadii,constnsIntRect&aContainedRect){// rectFullHeight and rectFullWidth together will approximately contain// the total area of the frame minus the rounded corners.nsIntRectrectFullHeight=aRoundedRect;uint32_txDiff=std::max(aCornerRadii.TopLeft().width,aCornerRadii.BottomLeft().width);rectFullHeight.x+=xDiff;rectFullHeight.width-=std::max(aCornerRadii.TopRight().width,aCornerRadii.BottomRight().width)+xDiff;nsIntRectr1;r1.IntersectRect(rectFullHeight,aContainedRect);nsIntRectrectFullWidth=aRoundedRect;uint32_tyDiff=std::max(aCornerRadii.TopLeft().height,aCornerRadii.TopRight().height);rectFullWidth.y+=yDiff;rectFullWidth.height-=std::max(aCornerRadii.BottomLeft().height,aCornerRadii.BottomRight().height)+yDiff;nsIntRectr2;r2.IntersectRect(rectFullWidth,aContainedRect);nsIntRegionresult;result.Or(r1,r2);returnresult;}// Helper for RoundedRectIntersectsRect.staticboolCheckCorner(nscoordaXOffset,nscoordaYOffset,nscoordaXRadius,nscoordaYRadius){MOZ_ASSERT(aXOffset>0&&aYOffset>0,"must not pass nonpositives to CheckCorner");MOZ_ASSERT(aXRadius>=0&&aYRadius>=0,"must not pass negatives to CheckCorner");// Avoid floating point math unless we're either (1) within the// quarter-ellipse area at the rounded corner or (2) outside the// rounding.if(aXOffset>=aXRadius||aYOffset>=aYRadius)returntrue;// Convert coordinates to a unit circle with (0,0) as the center of// curvature, and see if we're inside the circle or outside.floatscaledX=float(aXRadius-aXOffset)/float(aXRadius);floatscaledY=float(aYRadius-aYOffset)/float(aYRadius);returnscaledX*scaledX+scaledY*scaledY<1.0f;}boolnsLayoutUtils::RoundedRectIntersectsRect(constnsRect&aRoundedRect,constnscoordaRadii[8],constnsRect&aTestRect){if(!aTestRect.Intersects(aRoundedRect))returnfalse;// distances from this edge of aRoundedRect to opposite edge of aTestRect,// which we know are positive due to the Intersects check above.nsMargininsets;insets.top=aTestRect.YMost()-aRoundedRect.y;insets.right=aRoundedRect.XMost()-aTestRect.x;insets.bottom=aRoundedRect.YMost()-aTestRect.y;insets.left=aTestRect.XMost()-aRoundedRect.x;// Check whether the bottom-right corner of aTestRect is inside the// top left corner of aBounds when rounded by aRadii, etc. If any// corner is not, then fail; otherwise succeed.returnCheckCorner(insets.left,insets.top,aRadii[eCornerTopLeftX],aRadii[eCornerTopLeftY])&&CheckCorner(insets.right,insets.top,aRadii[eCornerTopRightX],aRadii[eCornerTopRightY])&&CheckCorner(insets.right,insets.bottom,aRadii[eCornerBottomRightX],aRadii[eCornerBottomRightY])&&CheckCorner(insets.left,insets.bottom,aRadii[eCornerBottomLeftX],aRadii[eCornerBottomLeftY]);}nsRectnsLayoutUtils::MatrixTransformRect(constnsRect&aBounds,constMatrix4x4&aMatrix,floataFactor){RectDoubleimage=RectDouble(NSAppUnitsToDoublePixels(aBounds.x,aFactor),NSAppUnitsToDoublePixels(aBounds.y,aFactor),NSAppUnitsToDoublePixels(aBounds.width,aFactor),NSAppUnitsToDoublePixels(aBounds.height,aFactor));RectDoublemaxBounds=RectDouble(double(nscoord_MIN)/aFactor*0.5,double(nscoord_MIN)/aFactor*0.5,double(nscoord_MAX)/aFactor,double(nscoord_MAX)/aFactor);image=aMatrix.TransformAndClipBounds(image,maxBounds);returnRoundGfxRectToAppRect(ThebesRect(image),aFactor);}nsPointnsLayoutUtils::MatrixTransformPoint(constnsPoint&aPoint,constMatrix4x4&aMatrix,floataFactor){gfxPointimage=gfxPoint(NSAppUnitsToFloatPixels(aPoint.x,aFactor),NSAppUnitsToFloatPixels(aPoint.y,aFactor));image=aMatrix.TransformPoint(image);returnnsPoint(NSFloatPixelsToAppUnits(float(image.x),aFactor),NSFloatPixelsToAppUnits(float(image.y),aFactor));}voidnsLayoutUtils::PostTranslate(Matrix4x4&aTransform,constnsPoint&aOrigin,floataAppUnitsPerPixel,boolaRounded){Point3DgfxOrigin=Point3D(NSAppUnitsToFloatPixels(aOrigin.x,aAppUnitsPerPixel),NSAppUnitsToFloatPixels(aOrigin.y,aAppUnitsPerPixel),0.0f);if(aRounded){gfxOrigin.x=NS_round(gfxOrigin.x);gfxOrigin.y=NS_round(gfxOrigin.y);}aTransform.PostTranslate(gfxOrigin);}Matrix4x4nsLayoutUtils::GetTransformToAncestor(nsIFrame*aFrame,constnsIFrame*aAncestor,boolaInCSSUnits){nsIFrame*parent;Matrix4x4ctm;if(aFrame==aAncestor){returnctm;}ctm=aFrame->GetTransformMatrix(aAncestor,&parent,aInCSSUnits);while(parent&&parent!=aAncestor){if(!parent->Extend3DContext()){ctm.ProjectTo2D();}ctm=ctm*parent->GetTransformMatrix(aAncestor,&parent,aInCSSUnits);}returnctm;}gfxSizensLayoutUtils::GetTransformToAncestorScale(nsIFrame*aFrame){Matrix4x4transform=GetTransformToAncestor(aFrame,nsLayoutUtils::GetDisplayRootFrame(aFrame));Matrixtransform2D;if(transform.Is2D(&transform2D)){returnThebesMatrix(transform2D).ScaleFactors(true);}returngfxSize(1,1);}staticMatrix4x4GetTransformToAncestorExcludingAnimated(nsIFrame*aFrame,constnsIFrame*aAncestor){nsIFrame*parent;Matrix4x4ctm;if(aFrame==aAncestor){returnctm;}if(ActiveLayerTracker::IsScaleSubjectToAnimation(aFrame)){returnctm;}ctm=aFrame->GetTransformMatrix(aAncestor,&parent);while(parent&&parent!=aAncestor){if(ActiveLayerTracker::IsScaleSubjectToAnimation(parent)){returnMatrix4x4();}if(!parent->Extend3DContext()){ctm.ProjectTo2D();}ctm=ctm*parent->GetTransformMatrix(aAncestor,&parent);}returnctm;}gfxSizensLayoutUtils::GetTransformToAncestorScaleExcludingAnimated(nsIFrame*aFrame){Matrix4x4transform=GetTransformToAncestorExcludingAnimated(aFrame,nsLayoutUtils::GetDisplayRootFrame(aFrame));Matrixtransform2D;if(transform.Is2D(&transform2D)){returnThebesMatrix(transform2D).ScaleFactors(true);}returngfxSize(1,1);}nsIFrame*nsLayoutUtils::FindNearestCommonAncestorFrame(nsIFrame*aFrame1,nsIFrame*aFrame2){AutoTArray<nsIFrame*,100>ancestors1;AutoTArray<nsIFrame*,100>ancestors2;nsIFrame*commonAncestor=nullptr;if(aFrame1->PresContext()==aFrame2->PresContext()){commonAncestor=aFrame1->PresContext()->PresShell()->GetRootFrame();}for(nsIFrame*f=aFrame1;f!=commonAncestor;f=nsLayoutUtils::GetCrossDocParentFrame(f)){ancestors1.AppendElement(f);}for(nsIFrame*f=aFrame2;f!=commonAncestor;f=nsLayoutUtils::GetCrossDocParentFrame(f)){ancestors2.AppendElement(f);}uint32_tminLengths=std::min(ancestors1.Length(),ancestors2.Length());for(uint32_ti=1;i<=minLengths;++i){if(ancestors1[ancestors1.Length()-i]==ancestors2[ancestors2.Length()-i]){commonAncestor=ancestors1[ancestors1.Length()-i];}else{break;}}returncommonAncestor;}nsLayoutUtils::TransformResultnsLayoutUtils::TransformPoints(nsIFrame*aFromFrame,nsIFrame*aToFrame,uint32_taPointCount,CSSPoint*aPoints){nsIFrame*nearestCommonAncestor=FindNearestCommonAncestorFrame(aFromFrame,aToFrame);if(!nearestCommonAncestor){returnNO_COMMON_ANCESTOR;}Matrix4x4downToDest=GetTransformToAncestor(aToFrame,nearestCommonAncestor);if(downToDest.IsSingular()){returnNONINVERTIBLE_TRANSFORM;}downToDest.Invert();Matrix4x4upToAncestor=GetTransformToAncestor(aFromFrame,nearestCommonAncestor);CSSToLayoutDeviceScaledevPixelsPerCSSPixelFromFrame=aFromFrame->PresContext()->CSSToDevPixelScale();CSSToLayoutDeviceScaledevPixelsPerCSSPixelToFrame=aToFrame->PresContext()->CSSToDevPixelScale();for(uint32_ti=0;i<aPointCount;++i){LayoutDevicePointdevPixels=aPoints[i]*devPixelsPerCSSPixelFromFrame;// What should the behaviour be if some of the points aren't invertible// and others are? Just assume all points are for now.PointtoDevPixels=downToDest.ProjectPoint((upToAncestor.TransformPoint(Point(devPixels.x,devPixels.y)))).As2DPoint();// Divide here so that when the devPixelsPerCSSPixels are the same, we get the correct// answer instead of some inaccuracy multiplying a number by its reciprocal.aPoints[i]=LayoutDevicePoint(toDevPixels.x,toDevPixels.y)/devPixelsPerCSSPixelToFrame;}returnTRANSFORM_SUCCEEDED;}nsLayoutUtils::TransformResultnsLayoutUtils::TransformPoint(nsIFrame*aFromFrame,nsIFrame*aToFrame,nsPoint&aPoint){nsIFrame*nearestCommonAncestor=FindNearestCommonAncestorFrame(aFromFrame,aToFrame);if(!nearestCommonAncestor){returnNO_COMMON_ANCESTOR;}Matrix4x4downToDest=GetTransformToAncestor(aToFrame,nearestCommonAncestor);if(downToDest.IsSingular()){returnNONINVERTIBLE_TRANSFORM;}downToDest.Invert();Matrix4x4upToAncestor=GetTransformToAncestor(aFromFrame,nearestCommonAncestor);floatdevPixelsPerAppUnitFromFrame=1.0f/aFromFrame->PresContext()->AppUnitsPerDevPixel();floatdevPixelsPerAppUnitToFrame=1.0f/aToFrame->PresContext()->AppUnitsPerDevPixel();Point4DtoDevPixels=downToDest.ProjectPoint(upToAncestor.TransformPoint(Point(aPoint.x*devPixelsPerAppUnitFromFrame,aPoint.y*devPixelsPerAppUnitFromFrame)));if(!toDevPixels.HasPositiveWCoord()){// Not strictly true, but we failed to get a valid point in this// coordinate space.returnNONINVERTIBLE_TRANSFORM;}aPoint.x=NSToCoordRound(toDevPixels.x/devPixelsPerAppUnitToFrame);aPoint.y=NSToCoordRound(toDevPixels.y/devPixelsPerAppUnitToFrame);returnTRANSFORM_SUCCEEDED;}nsLayoutUtils::TransformResultnsLayoutUtils::TransformRect(nsIFrame*aFromFrame,nsIFrame*aToFrame,nsRect&aRect){nsIFrame*nearestCommonAncestor=FindNearestCommonAncestorFrame(aFromFrame,aToFrame);if(!nearestCommonAncestor){returnNO_COMMON_ANCESTOR;}Matrix4x4downToDest=GetTransformToAncestor(aToFrame,nearestCommonAncestor);if(downToDest.IsSingular()){returnNONINVERTIBLE_TRANSFORM;}downToDest.Invert();Matrix4x4upToAncestor=GetTransformToAncestor(aFromFrame,nearestCommonAncestor);floatdevPixelsPerAppUnitFromFrame=1.0f/aFromFrame->PresContext()->AppUnitsPerDevPixel();floatdevPixelsPerAppUnitToFrame=1.0f/aToFrame->PresContext()->AppUnitsPerDevPixel();gfx::RecttoDevPixels=downToDest.ProjectRectBounds(upToAncestor.ProjectRectBounds(gfx::Rect(aRect.x*devPixelsPerAppUnitFromFrame,aRect.y*devPixelsPerAppUnitFromFrame,aRect.width*devPixelsPerAppUnitFromFrame,aRect.height*devPixelsPerAppUnitFromFrame),Rect(-std::numeric_limits<Float>::max()*0.5f,-std::numeric_limits<Float>::max()*0.5f,std::numeric_limits<Float>::max(),std::numeric_limits<Float>::max())),Rect(-std::numeric_limits<Float>::max()*devPixelsPerAppUnitFromFrame*0.5f,-std::numeric_limits<Float>::max()*devPixelsPerAppUnitFromFrame*0.5f,std::numeric_limits<Float>::max()*devPixelsPerAppUnitFromFrame,std::numeric_limits<Float>::max()*devPixelsPerAppUnitFromFrame));aRect.x=NSToCoordRound(toDevPixels.x/devPixelsPerAppUnitToFrame);aRect.y=NSToCoordRound(toDevPixels.y/devPixelsPerAppUnitToFrame);aRect.width=NSToCoordRound(toDevPixels.width/devPixelsPerAppUnitToFrame);aRect.height=NSToCoordRound(toDevPixels.height/devPixelsPerAppUnitToFrame);returnTRANSFORM_SUCCEEDED;}nsRectnsLayoutUtils::GetRectRelativeToFrame(Element*aElement,nsIFrame*aFrame){if(!aElement||!aFrame){returnnsRect();}nsIFrame*frame=aElement->GetPrimaryFrame();if(!frame){returnnsRect();}nsRectrect=frame->GetRectRelativeToSelf();nsLayoutUtils::TransformResultrv=nsLayoutUtils::TransformRect(frame,aFrame,rect);if(rv!=nsLayoutUtils::TRANSFORM_SUCCEEDED){returnnsRect();}returnrect;}boolnsLayoutUtils::ContainsPoint(constnsRect&aRect,constnsPoint&aPoint,nscoordaInflateSize){nsRectrect=aRect;rect.Inflate(aInflateSize);returnrect.Contains(aPoint);}nsRectnsLayoutUtils::ClampRectToScrollFrames(nsIFrame*aFrame,constnsRect&aRect){nsIFrame*closestScrollFrame=nsLayoutUtils::GetClosestFrameOfType(aFrame,LayoutFrameType::Scroll);nsRectresultRect=aRect;while(closestScrollFrame){nsIScrollableFrame*sf=do_QueryFrame(closestScrollFrame);nsRectscrollPortRect=sf->GetScrollPortRect();nsLayoutUtils::TransformRect(closestScrollFrame,aFrame,scrollPortRect);resultRect=resultRect.Intersect(scrollPortRect);// Check whether aRect is visible in the scroll frame or not.if(resultRect.IsEmpty()){break;}// Get next ancestor scroll frame.closestScrollFrame=nsLayoutUtils::GetClosestFrameOfType(closestScrollFrame->GetParent(),LayoutFrameType::Scroll);}returnresultRect;}boolnsLayoutUtils::GetLayerTransformForFrame(nsIFrame*aFrame,Matrix4x4*aTransform){// FIXME/bug 796690: we can sometimes compute a transform in these// cases, it just increases complexity considerably. Punt for now.if(aFrame->Extend3DContext()||aFrame->HasTransformGetter()){returnfalse;}nsIFrame*root=nsLayoutUtils::GetDisplayRootFrame(aFrame);if(root->HasAnyStateBits(NS_FRAME_UPDATE_LAYER_TREE)){// Content may have been invalidated, so we can't reliably compute// the "layer transform" in general.returnfalse;}// If the caller doesn't care about the value, early-return to skip// overhead below.if(!aTransform){returntrue;}nsDisplayListBuilderbuilder(root,nsDisplayListBuilderMode::TRANSFORM_COMPUTATION,false/*don't build caret*/);nsDisplayListlist;nsDisplayTransform*item=new(&builder)nsDisplayTransform(&builder,aFrame,&list,nsRect());*aTransform=item->GetTransform();item->~nsDisplayTransform();returntrue;}staticboolTransformGfxPointFromAncestor(nsIFrame*aFrame,constPoint&aPoint,nsIFrame*aAncestor,Point*aOut){Matrix4x4ctm=nsLayoutUtils::GetTransformToAncestor(aFrame,aAncestor);ctm.Invert();Point4Dpoint=ctm.ProjectPoint(aPoint);if(!point.HasPositiveWCoord()){returnfalse;}*aOut=point.As2DPoint();returntrue;}staticRectTransformGfxRectToAncestor(nsIFrame*aFrame,constRect&aRect,constnsIFrame*aAncestor,bool*aPreservesAxisAlignedRectangles=nullptr,Maybe<Matrix4x4>*aMatrixCache=nullptr){Matrix4x4ctm;if(aMatrixCache&&*aMatrixCache){// We are given a matrix to use, so use itctm=aMatrixCache->value();}else{// Else, compute itctm=nsLayoutUtils::GetTransformToAncestor(aFrame,aAncestor);if(aMatrixCache){// and put it in the cache, if provided*aMatrixCache=Some(ctm);}}// Fill out the axis-alignment flagif(aPreservesAxisAlignedRectangles){Matrixmatrix2d;*aPreservesAxisAlignedRectangles=ctm.Is2D(&matrix2d)&&matrix2d.PreservesAxisAlignedRectangles();}RectmaxBounds=Rect(-std::numeric_limits<float>::max()*0.5,-std::numeric_limits<float>::max()*0.5,std::numeric_limits<float>::max(),std::numeric_limits<float>::max());returnctm.TransformAndClipBounds(aRect,maxBounds);}staticSVGTextFrame*GetContainingSVGTextFrame(nsIFrame*aFrame){if(!nsSVGUtils::IsInSVGTextSubtree(aFrame)){returnnullptr;}returnstatic_cast<SVGTextFrame*>(nsLayoutUtils::GetClosestFrameOfType(aFrame->GetParent(),LayoutFrameType::SVGText));}nsPointnsLayoutUtils::TransformAncestorPointToFrame(nsIFrame*aFrame,constnsPoint&aPoint,nsIFrame*aAncestor){SVGTextFrame*text=GetContainingSVGTextFrame(aFrame);floatfactor=aFrame->PresContext()->AppUnitsPerDevPixel();Pointresult(NSAppUnitsToFloatPixels(aPoint.x,factor),NSAppUnitsToFloatPixels(aPoint.y,factor));if(text){if(!TransformGfxPointFromAncestor(text,result,aAncestor,&result)){returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);}result=text->TransformFramePointToTextChild(result,aFrame);}else{if(!TransformGfxPointFromAncestor(aFrame,result,nullptr,&result)){returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);}}returnnsPoint(NSFloatPixelsToAppUnits(float(result.x),factor),NSFloatPixelsToAppUnits(float(result.y),factor));}nsRectnsLayoutUtils::TransformFrameRectToAncestor(nsIFrame*aFrame,constnsRect&aRect,constnsIFrame*aAncestor,bool*aPreservesAxisAlignedRectangles/* = nullptr */,Maybe<Matrix4x4>*aMatrixCache/* = nullptr */){SVGTextFrame*text=GetContainingSVGTextFrame(aFrame);floatsrcAppUnitsPerDevPixel=aFrame->PresContext()->AppUnitsPerDevPixel();Rectresult;if(text){result=ToRect(text->TransformFrameRectFromTextChild(aRect,aFrame));result=TransformGfxRectToAncestor(text,result,aAncestor,nullptr,aMatrixCache);// TransformFrameRectFromTextChild could involve any kind of transform, we// could drill down into it to get an answer out of it but we don't yet.if(aPreservesAxisAlignedRectangles)*aPreservesAxisAlignedRectangles=false;}else{result=Rect(NSAppUnitsToFloatPixels(aRect.x,srcAppUnitsPerDevPixel),NSAppUnitsToFloatPixels(aRect.y,srcAppUnitsPerDevPixel),NSAppUnitsToFloatPixels(aRect.width,srcAppUnitsPerDevPixel),NSAppUnitsToFloatPixels(aRect.height,srcAppUnitsPerDevPixel));result=TransformGfxRectToAncestor(aFrame,result,aAncestor,aPreservesAxisAlignedRectangles,aMatrixCache);}floatdestAppUnitsPerDevPixel=aAncestor->PresContext()->AppUnitsPerDevPixel();returnnsRect(NSFloatPixelsToAppUnits(float(result.x),destAppUnitsPerDevPixel),NSFloatPixelsToAppUnits(float(result.y),destAppUnitsPerDevPixel),NSFloatPixelsToAppUnits(float(result.width),destAppUnitsPerDevPixel),NSFloatPixelsToAppUnits(float(result.height),destAppUnitsPerDevPixel));}staticLayoutDeviceIntPointGetWidgetOffset(nsIWidget*aWidget,nsIWidget*&aRootWidget){LayoutDeviceIntPointoffset(0,0);while((aWidget->WindowType()==eWindowType_child||aWidget->IsPlugin())){nsIWidget*parent=aWidget->GetParent();if(!parent){break;}LayoutDeviceIntRectbounds=aWidget->GetBounds();offset+=bounds.TopLeft();aWidget=parent;}aRootWidget=aWidget;returnoffset;}staticLayoutDeviceIntPointWidgetToWidgetOffset(nsIWidget*aFrom,nsIWidget*aTo){nsIWidget*fromRoot;LayoutDeviceIntPointfromOffset=GetWidgetOffset(aFrom,fromRoot);nsIWidget*toRoot;LayoutDeviceIntPointtoOffset=GetWidgetOffset(aTo,toRoot);if(fromRoot==toRoot){returnfromOffset-toOffset;}returnaFrom->WidgetToScreenOffset()-aTo->WidgetToScreenOffset();}nsPointnsLayoutUtils::TranslateWidgetToView(nsPresContext*aPresContext,nsIWidget*aWidget,constLayoutDeviceIntPoint&aPt,nsView*aView){nsPointviewOffset;nsIWidget*viewWidget=aView->GetNearestWidget(&viewOffset);if(!viewWidget){returnnsPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);}LayoutDeviceIntPointwidgetPoint=aPt+WidgetToWidgetOffset(aWidget,viewWidget);nsPointwidgetAppUnits(aPresContext->DevPixelsToAppUnits(widgetPoint.x),aPresContext->DevPixelsToAppUnits(widgetPoint.y));returnwidgetAppUnits-viewOffset;}LayoutDeviceIntPointnsLayoutUtils::TranslateViewToWidget(nsPresContext*aPresContext,nsView*aView,nsPointaPt,nsIWidget*aWidget){nsPointviewOffset;nsIWidget*viewWidget=aView->GetNearestWidget(&viewOffset);if(!viewWidget){returnLayoutDeviceIntPoint(NS_UNCONSTRAINEDSIZE,NS_UNCONSTRAINEDSIZE);}nsPointpt=(aPt+viewOffset).ApplyResolution(GetCurrentAPZResolutionScale(aPresContext->PresShell()));LayoutDeviceIntPointrelativeToViewWidget(aPresContext->AppUnitsToDevPixels(pt.x),aPresContext->AppUnitsToDevPixels(pt.y));returnrelativeToViewWidget+WidgetToWidgetOffset(viewWidget,aWidget);}// Combine aNewBreakType with aOrigBreakType, but limit the break types// to StyleClear::Left, Right, Both.StyleClearnsLayoutUtils::CombineBreakType(StyleClearaOrigBreakType,StyleClearaNewBreakType){StyleClearbreakType=aOrigBreakType;switch(breakType){caseStyleClear::Left:if(StyleClear::Right==aNewBreakType||StyleClear::Both==aNewBreakType){breakType=StyleClear::Both;}break;caseStyleClear::Right:if(StyleClear::Left==aNewBreakType||StyleClear::Both==aNewBreakType){breakType=StyleClear::Both;}break;caseStyleClear::None:if(StyleClear::Left==aNewBreakType||StyleClear::Right==aNewBreakType||StyleClear::Both==aNewBreakType){breakType=aNewBreakType;}break;default:break;}returnbreakType;}#ifdef MOZ_DUMP_PAINTING#include<stdio.h>staticboolgDumpEventList=false;// nsLayoutUtils::PaintFrame() can call itself recursively, so rather than// maintaining a single paint count, we need a stack.StaticAutoPtr<nsTArray<int>>gPaintCountStack;structAutoNestedPaintCount{AutoNestedPaintCount(){gPaintCountStack->AppendElement(0);}~AutoNestedPaintCount(){gPaintCountStack->RemoveElementAt(gPaintCountStack->Length()-1);}};#endifnsIFrame*nsLayoutUtils::GetFrameForPoint(nsIFrame*aFrame,nsPointaPt,uint32_taFlags){AUTO_PROFILER_LABEL("nsLayoutUtils::GetFrameForPoint",GRAPHICS);nsresultrv;AutoTArray<nsIFrame*,8>outFrames;rv=GetFramesForArea(aFrame,nsRect(aPt,nsSize(1,1)),outFrames,aFlags);NS_ENSURE_SUCCESS(rv,nullptr);returnoutFrames.Length()?outFrames.ElementAt(0):nullptr;}nsresultnsLayoutUtils::GetFramesForArea(nsIFrame*aFrame,constnsRect&aRect,nsTArray<nsIFrame*>&aOutFrames,uint32_taFlags){AUTO_PROFILER_LABEL("nsLayoutUtils::GetFramesForArea",GRAPHICS);nsDisplayListBuilderbuilder(aFrame,nsDisplayListBuilderMode::EVENT_DELIVERY,false);nsDisplayListlist;if(aFlags&IGNORE_PAINT_SUPPRESSION){builder.IgnorePaintSuppression();}if(aFlags&IGNORE_ROOT_SCROLL_FRAME){nsIFrame*rootScrollFrame=aFrame->PresContext()->PresShell()->GetRootScrollFrame();if(rootScrollFrame){builder.SetIgnoreScrollFrame(rootScrollFrame);}}if(aFlags&IGNORE_CROSS_DOC){builder.SetDescendIntoSubdocuments(false);}builder.EnterPresShell(aFrame);aFrame->BuildDisplayListForStackingContext(&builder,aRect,&list);builder.LeavePresShell(aFrame,nullptr);#ifdef MOZ_DUMP_PAINTINGif(gDumpEventList){fprintf_stderr(stderr,"Event handling --- (%d,%d):\n",aRect.x,aRect.y);std::stringstreamss;nsFrame::PrintDisplayList(&builder,list,ss);print_stderr(ss);}#endifnsDisplayItem::HitTestStatehitTestState;builder.SetHitTestShouldStopAtFirstOpaque(aFlags&ONLY_VISIBLE);list.HitTest(&builder,aRect,&hitTestState,&aOutFrames);list.DeleteAll();returnNS_OK;}// aScrollFrameAsScrollable must be non-nullptr and queryable to an nsIFrameFrameMetricsnsLayoutUtils::CalculateBasicFrameMetrics(nsIScrollableFrame*aScrollFrame){nsIFrame*frame=do_QueryFrame(aScrollFrame);MOZ_ASSERT(frame);// Calculate the metrics necessary for calculating the displayport.// This code has a lot in common with the code in ComputeFrameMetrics();// we may want to refactor this at some point.FrameMetricsmetrics;nsPresContext*presContext=frame->PresContext();nsIPresShell*presShell=presContext->PresShell();CSSToLayoutDeviceScaledeviceScale=presContext->CSSToDevPixelScale();floatresolution=1.0f;if(frame==presShell->GetRootScrollFrame()){// Only the root scrollable frame for a given presShell should pick up// the presShell's resolution. All the other frames are 1.0.resolution=presShell->GetResolution();}// Note: unlike in ComputeFrameMetrics(), we don't know the full cumulative// resolution including FrameMetrics::mExtraResolution, because layout hasn't// chosen a resolution to paint at yet. However, the display port calculation// divides out mExtraResolution anyways, so we get the correct result by// setting the mCumulativeResolution to everything except the extra resolution// and leaving mExtraResolution at 1.LayoutDeviceToLayerScale2DcumulativeResolution(presShell->GetCumulativeResolution()*nsLayoutUtils::GetTransformToAncestorScale(frame));LayerToParentLayerScalelayerToParentLayerScale(1.0f);metrics.SetDevPixelsPerCSSPixel(deviceScale);metrics.SetPresShellResolution(resolution);metrics.SetCumulativeResolution(cumulativeResolution);metrics.SetZoom(deviceScale*cumulativeResolution*layerToParentLayerScale);// Only the size of the composition bounds is relevant to the// displayport calculation, not its origin.nsSizecompositionSize=nsLayoutUtils::CalculateCompositionSizeForFrame(frame);LayoutDeviceToParentLayerScale2DcompBoundsScale;if(frame==presShell->GetRootScrollFrame()&&presContext->IsRootContentDocument()){if(presContext->GetParentPresContext()){floatres=presContext->GetParentPresContext()->PresShell()->GetCumulativeResolution();compBoundsScale=LayoutDeviceToParentLayerScale2D(LayoutDeviceToParentLayerScale(res));}}else{compBoundsScale=cumulativeResolution*layerToParentLayerScale;}metrics.SetCompositionBounds(LayoutDeviceRect::FromAppUnits(nsRect(nsPoint(0,0),compositionSize),presContext->AppUnitsPerDevPixel())*compBoundsScale);metrics.SetRootCompositionSize(nsLayoutUtils::CalculateRootCompositionSize(frame,false,metrics));metrics.SetScrollOffset(CSSPoint::FromAppUnits(aScrollFrame->GetScrollPosition()));metrics.SetScrollableRect(CSSRect::FromAppUnits(nsLayoutUtils::CalculateScrollableRectForFrame(aScrollFrame,nullptr)));returnmetrics;}boolnsLayoutUtils::CalculateAndSetDisplayPortMargins(nsIScrollableFrame*aScrollFrame,RepaintModeaRepaintMode){nsIFrame*frame=do_QueryFrame(aScrollFrame);MOZ_ASSERT(frame);nsIContent*content=frame->GetContent();MOZ_ASSERT(content);FrameMetricsmetrics=CalculateBasicFrameMetrics(aScrollFrame);ScreenMargindisplayportMargins=APZCTreeManager::CalculatePendingDisplayPort(metrics,ParentLayerPoint(0.0f,0.0f));nsIPresShell*presShell=frame->PresContext()->GetPresShell();returnnsLayoutUtils::SetDisplayPortMargins(content,presShell,displayportMargins,0,aRepaintMode);}voidnsLayoutUtils::MaybeCreateDisplayPort(nsDisplayListBuilder&aBuilder,nsIFrame*aScrollFrame){nsIContent*content=aScrollFrame->GetContent();nsIScrollableFrame*scrollableFrame=do_QueryFrame(aScrollFrame);if(!content||!scrollableFrame){return;}boolhaveDisplayPort=HasDisplayPort(content);// We perform an optimization where we ensure that at least one// async-scrollable frame (i.e. one that WantsAsyncScroll()) has a displayport.// If that's not the case yet, and we are async-scrollable, we will get a// displayport.if(aBuilder.IsPaintingToWindow()&&nsLayoutUtils::AsyncPanZoomEnabled(aScrollFrame)&&!aBuilder.HaveScrollableDisplayPort()&&scrollableFrame->WantAsyncScroll()){// If we don't already have a displayport, calculate and set one.if(!haveDisplayPort){CalculateAndSetDisplayPortMargins(scrollableFrame,nsLayoutUtils::RepaintMode::DoNotRepaint);#ifdef DEBUGhaveDisplayPort=HasDisplayPort(content);MOZ_ASSERT(haveDisplayPort,"should have a displayport after having just set it");#endif}// Record that the we now have a scrollable display port.aBuilder.SetHaveScrollableDisplayPort();}}nsIScrollableFrame*nsLayoutUtils::GetAsyncScrollableAncestorFrame(nsIFrame*aTarget){uint32_tflags=nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT|nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE|nsLayoutUtils::SCROLLABLE_FIXEDPOS_FINDS_ROOT;returnnsLayoutUtils::GetNearestScrollableFrame(aTarget,flags);}voidnsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(nsIFrame*aFrame,RepaintModeaRepaintMode){nsIFrame*frame=aFrame;while(frame){frame=nsLayoutUtils::GetCrossDocParentFrame(frame);if(!frame){break;}nsIScrollableFrame*scrollAncestor=GetAsyncScrollableAncestorFrame(frame);if(!scrollAncestor){break;}frame=do_QueryFrame(scrollAncestor);MOZ_ASSERT(frame);MOZ_ASSERT(scrollAncestor->WantAsyncScroll()||frame->PresContext()->PresShell()->GetRootScrollFrame()==frame);if(nsLayoutUtils::AsyncPanZoomEnabled(frame)&&!nsLayoutUtils::HasDisplayPort(frame->GetContent())){nsLayoutUtils::SetDisplayPortMargins(frame->GetContent(),frame->PresContext()->PresShell(),ScreenMargin(),0,aRepaintMode);}}}voidnsLayoutUtils::ExpireDisplayPortOnAsyncScrollableAncestor(nsIFrame*aFrame){nsIFrame*frame=aFrame;while(frame){frame=nsLayoutUtils::GetCrossDocParentFrame(frame);if(!frame){break;}nsIScrollableFrame*scrollAncestor=GetAsyncScrollableAncestorFrame(frame);if(!scrollAncestor){break;}frame=do_QueryFrame(scrollAncestor);MOZ_ASSERT(frame);if(!frame){break;}MOZ_ASSERT(scrollAncestor->WantAsyncScroll()||frame->PresContext()->PresShell()->GetRootScrollFrame()==frame);if(nsLayoutUtils::HasDisplayPort(frame->GetContent())){scrollAncestor->TriggerDisplayPortExpiration();// Stop after the first trigger. If it failed, there's no point in// continuing because all the rest of the frames we encounter are going// to be ancestors of |scrollAncestor| which will keep its displayport.// If the trigger succeeded, we stop because when the trigger executes// it will call this function again to trigger the next ancestor up the// chain.break;}}}nsresultnsLayoutUtils::PaintFrame(gfxContext*aRenderingContext,nsIFrame*aFrame,constnsRegion&aDirtyRegion,nscoloraBackstop,nsDisplayListBuilderModeaBuilderMode,PaintFrameFlagsaFlags){AUTO_PROFILER_LABEL("nsLayoutUtils::PaintFrame",GRAPHICS);#ifdef MOZ_DUMP_PAINTINGif(!gPaintCountStack){gPaintCountStack=newnsTArray<int>();ClearOnShutdown(&gPaintCountStack);gPaintCountStack->AppendElement(0);}++gPaintCountStack->LastElement();AutoNestedPaintCountnestedPaintCount;#endifif(aFlags&PaintFrameFlags::PAINT_WIDGET_LAYERS){nsView*view=aFrame->GetView();if(!(view&&view->GetWidget()&&GetDisplayRootFrame(aFrame)==aFrame)){aFlags&=~PaintFrameFlags::PAINT_WIDGET_LAYERS;NS_ASSERTION(aRenderingContext,"need a rendering context");}}nsPresContext*presContext=aFrame->PresContext();nsIPresShell*presShell=presContext->PresShell();nsRootPresContext*rootPresContext=presContext->GetRootPresContext();if(!rootPresContext){returnNS_OK;}TimeStampstartBuildDisplayList=TimeStamp::Now();nsDisplayListBuilderbuilder(aFrame,aBuilderMode,!(aFlags&PaintFrameFlags::PAINT_HIDE_CARET));if(aFlags&PaintFrameFlags::PAINT_IN_TRANSFORM){builder.SetInTransform(true);}if(aFlags&PaintFrameFlags::PAINT_SYNC_DECODE_IMAGES){builder.SetSyncDecodeImages(true);}if(aFlags&(PaintFrameFlags::PAINT_WIDGET_LAYERS|PaintFrameFlags::PAINT_TO_WINDOW)){builder.SetPaintingToWindow(true);}if(aFlags&PaintFrameFlags::PAINT_IGNORE_SUPPRESSION){builder.IgnorePaintSuppression();}nsIFrame*rootScrollFrame=presShell->GetRootScrollFrame();if(rootScrollFrame&&!aFrame->GetParent()){nsIScrollableFrame*rootScrollableFrame=presShell->GetRootScrollFrameAsScrollable();MOZ_ASSERT(rootScrollableFrame);nsRectdisplayPortBase=aFrame->GetVisualOverflowRectRelativeToSelf();Unused<<rootScrollableFrame->DecideScrollableLayer(&builder,&displayPortBase,/* aAllowCreateDisplayPort = */true);}nsRegionvisibleRegion;if(aFlags&PaintFrameFlags::PAINT_WIDGET_LAYERS){// This layer tree will be reused, so we'll need to calculate it// for the whole "visible" area of the window//// |ignoreViewportScrolling| and |usingDisplayPort| are persistent// document-rendering state. We rely on PresShell to flush// retained layers as needed when that persistent state changes.visibleRegion=aFrame->GetVisualOverflowRectRelativeToSelf();}else{visibleRegion=aDirtyRegion;}nsDisplayListlist;// If the root has embedded plugins, flag the builder so we know we'll need// to update plugin geometry after painting.if((aFlags&PaintFrameFlags::PAINT_WIDGET_LAYERS)&&!(aFlags&PaintFrameFlags::PAINT_DOCUMENT_RELATIVE)&&rootPresContext->NeedToComputePluginGeometryUpdates()){builder.SetWillComputePluginGeometry(true);}nsRectcanvasArea(nsPoint(0,0),aFrame->GetSize());boolignoreViewportScrolling=aFrame->GetParent()?false:presShell->IgnoringViewportScrolling();if(ignoreViewportScrolling&&rootScrollFrame){nsIScrollableFrame*rootScrollableFrame=presShell->GetRootScrollFrameAsScrollable();if(aFlags&PaintFrameFlags::PAINT_DOCUMENT_RELATIVE){// Make visibleRegion and aRenderingContext relative to the// scrolled frame instead of the root frame.nsPointpos=rootScrollableFrame->GetScrollPosition();visibleRegion.MoveBy(-pos);if(aRenderingContext){gfxPointdevPixelOffset=nsLayoutUtils::PointToGfxPoint(pos,presContext->AppUnitsPerDevPixel());aRenderingContext->SetMatrix(aRenderingContext->CurrentMatrix().Translate(devPixelOffset));}}builder.SetIgnoreScrollFrame(rootScrollFrame);nsCanvasFrame*canvasFrame=do_QueryFrame(rootScrollableFrame->GetScrolledFrame());if(canvasFrame){// Use UnionRect here to ensure that areas where the scrollbars// were are still filled with the background color.canvasArea.UnionRect(canvasArea,canvasFrame->CanvasArea()+builder.ToReferenceFrame(canvasFrame));}}nsRectdirtyRect=visibleRegion.GetBounds();{AUTO_PROFILER_LABEL("nsLayoutUtils::PaintFrame:BuildDisplayList",GRAPHICS);AutoProfilerTracingtracing("Paint","DisplayList");PaintTelemetry::AutoRecordrecord(PaintTelemetry::Metric::DisplayList);builder.EnterPresShell(aFrame);{// If a scrollable container layer is created in nsDisplayList::PaintForFrame,// it will be the scroll parent for display items that are built in the// BuildDisplayListForStackingContext call below. We need to set the scroll// parent on the display list builder while we build those items, so that they// can pick up their scroll parent's id.ViewIDid=FrameMetrics::NULL_SCROLL_ID;if(ignoreViewportScrolling&&presContext->IsRootContentDocument()){if(nsIFrame*rootScrollFrame=presShell->GetRootScrollFrame()){if(nsIContent*content=rootScrollFrame->GetContent()){id=nsLayoutUtils::FindOrCreateIDFor(content);}}}elseif(presShell->GetDocument()&&presShell->GetDocument()->IsRootDisplayDocument()&&!presShell->GetRootScrollFrame()){// In cases where the root document is a XUL document, we want to take// the ViewID from the root element, as that will be the ViewID of the// root APZC in the tree. Skip doing this in cases where we know// nsGfxScrollFrame::BuilDisplayList will do it instead.if(dom::Element*element=presShell->GetDocument()->GetDocumentElement()){id=nsLayoutUtils::FindOrCreateIDFor(element);}}nsDisplayListBuilder::AutoCurrentScrollParentIdSetteridSetter(&builder,id);aFrame->BuildDisplayListForStackingContext(&builder,dirtyRect,&list);}LayoutFrameTypeframeType=aFrame->Type();// For the viewport frame in print preview/page layout we want to paint// the grey background behind the page, not the canvas color.if(frameType==LayoutFrameType::Viewport&&nsLayoutUtils::NeedsPrintPreviewBackground(presContext)){nsRectbounds=nsRect(builder.ToReferenceFrame(aFrame),aFrame->GetSize());nsDisplayListBuilder::AutoBuildingDisplayListbuildingDisplayList(&builder,aFrame,bounds,false);presShell->AddPrintPreviewBackgroundItem(builder,list,aFrame,bounds);}elseif(frameType!=LayoutFrameType::Page){// For printing, this function is first called on an nsPageFrame, which// creates a display list with a PageContent item. The PageContent item's// paint function calls this function on the nsPageFrame's child which is// an nsPageContentFrame. We only want to add the canvas background color// item once, for the nsPageContentFrame.// Add the canvas background color to the bottom of the list. This// happens after we've built the list so that AddCanvasBackgroundColorItem// can monkey with the contents if necessary.canvasArea.IntersectRect(canvasArea,visibleRegion.GetBounds());nsDisplayListBuilder::AutoBuildingDisplayListbuildingDisplayList(&builder,aFrame,canvasArea,false);presShell->AddCanvasBackgroundColorItem(builder,list,aFrame,canvasArea,aBackstop);}builder.LeavePresShell(aFrame,&list);if(!record.GetStart().IsNull()&&gfxPrefs::LayersDrawFPS()){if(RefPtr<LayerManager>lm=builder.GetWidgetLayerManager()){if(PaintTiming*pt=ClientLayerManager::MaybeGetPaintTiming(lm)){pt->dlMs()=(TimeStamp::Now()-record.GetStart()).ToMilliseconds();}}}}Telemetry::AccumulateTimeDelta(Telemetry::PAINT_BUILD_DISPLAYLIST_TIME,startBuildDisplayList);boolprofilerNeedsDisplayList=profiler_feature_active(ProfilerFeature::DisplayListDump);boolconsoleNeedsDisplayList=gfxUtils::DumpDisplayList()||gfxEnv::DumpPaint();#ifdef MOZ_DUMP_PAINTINGFILE*savedDumpFile=gfxUtils::sDumpPaintFile;#endifUniquePtr<std::stringstream>ss;if(consoleNeedsDisplayList||profilerNeedsDisplayList){ss=MakeUnique<std::stringstream>();#ifdef MOZ_DUMP_PAINTINGif(gfxEnv::DumpPaintToFile()){nsCStringstring("dump-");// Include the process ID in the dump file name, to make sure that in an// e10s setup different processes don't clobber each other's dump files.string.AppendInt(getpid());for(intpaintCount:*gPaintCountStack){string.AppendLiteral("-");string.AppendInt(paintCount);}string.AppendLiteral(".html");gfxUtils::sDumpPaintFile=fopen(string.BeginReading(),"w");}else{gfxUtils::sDumpPaintFile=stderr;}if(gfxEnv::DumpPaintToFile()){*ss<<"<html><head><script>\n""var array = {};\n""function ViewImage(index) { \n"" var image = document.getElementById(index);\n"" if (image.src) {\n"" image.removeAttribute('src');\n"" } else {\n"" image.src = array[index];\n"" }\n""}</script></head><body>";}#endif*ss<<nsPrintfCString("Painting --- before optimization (dirty %d,%d,%d,%d):\n",dirtyRect.x,dirtyRect.y,dirtyRect.width,dirtyRect.height).get();nsFrame::PrintDisplayList(&builder,list,*ss,gfxEnv::DumpPaintToFile());if(gfxEnv::DumpPaint()||gfxEnv::DumpPaintItems()){// Flush stream now to avoid reordering dump output relative to// messages dumped by PaintRoot below.if(profilerNeedsDisplayList&&!consoleNeedsDisplayList){profiler_tracing("log",ss->str().c_str());}else{fprint_stderr(gfxUtils::sDumpPaintFile,*ss);}ss=MakeUnique<std::stringstream>();}}uint32_tflags=nsDisplayList::PAINT_DEFAULT;if(aFlags&PaintFrameFlags::PAINT_WIDGET_LAYERS){flags|=nsDisplayList::PAINT_USE_WIDGET_LAYERS;if(!(aFlags&PaintFrameFlags::PAINT_DOCUMENT_RELATIVE)){nsIWidget*widget=aFrame->GetNearestWidget();if(widget){// If we're finished building display list items for painting of the outermost// pres shell, notify the widget about any toolbars we've encountered.widget->UpdateThemeGeometries(builder.GetThemeGeometries());}}}if(aFlags&PaintFrameFlags::PAINT_EXISTING_TRANSACTION){flags|=nsDisplayList::PAINT_EXISTING_TRANSACTION;}if(aFlags&PaintFrameFlags::PAINT_NO_COMPOSITE){flags|=nsDisplayList::PAINT_NO_COMPOSITE;}if(aFlags&PaintFrameFlags::PAINT_COMPRESSED){flags|=nsDisplayList::PAINT_COMPRESSED;}TimeStamppaintStart=TimeStamp::Now();RefPtr<LayerManager>layerManager=list.PaintRoot(&builder,aRenderingContext,flags);Telemetry::AccumulateTimeDelta(Telemetry::PAINT_RASTERIZE_TIME,paintStart);if(gfxPrefs::GfxLoggingPaintedPixelCountEnabled()){TimeStampnow=TimeStamp::Now();floatrasterizeTime=(now-paintStart).ToMilliseconds();uint32_tpixelCount=layerManager->GetAndClearPaintedPixelCount();staticstd::vector<std::pair<TimeStamp,uint32_t>>history;if(pixelCount){history.push_back(std::make_pair(now,pixelCount));}uint32_tpaintedInLastSecond=0;for(autoi=history.begin();i!=history.end();i++){if((now-i->first).ToMilliseconds()>1000.0f){// more than 1000ms ago, don't count itcontinue;}if(paintedInLastSecond==0){// This is the first one in the last 1000ms, so drop everything earlierhistory.erase(history.begin(),i);i=history.begin();}paintedInLastSecond+=i->second;MOZ_ASSERT(paintedInLastSecond);// all historical pixel counts are > 0}printf_stderr("Painted %u pixels in %fms (%u in the last 1000ms)\n",pixelCount,rasterizeTime,paintedInLastSecond);}if(consoleNeedsDisplayList||profilerNeedsDisplayList){*ss<<"Painting --- after optimization:\n";nsFrame::PrintDisplayList(&builder,list,*ss,gfxEnv::DumpPaintToFile());*ss<<"Painting --- layer tree:\n";if(layerManager){FrameLayerBuilder::DumpRetainedLayerTree(layerManager,*ss,gfxEnv::DumpPaintToFile());}if(profilerNeedsDisplayList&&!consoleNeedsDisplayList){profiler_tracing("log",ss->str().c_str());}else{fprint_stderr(gfxUtils::sDumpPaintFile,*ss);}#ifdef MOZ_DUMP_PAINTINGif(gfxEnv::DumpPaintToFile()){*ss<<"</body></html>";}if(gfxEnv::DumpPaintToFile()){fclose(gfxUtils::sDumpPaintFile);}gfxUtils::sDumpPaintFile=savedDumpFile;#endifstd::stringstreamlsStream;nsFrame::PrintDisplayList(&builder,list,lsStream);layerManager->GetRoot()->SetDisplayListLog(lsStream.str().c_str());}#ifdef MOZ_DUMP_PAINTINGif(gfxPrefs::DumpClientLayers()){std::stringstreamss;FrameLayerBuilder::DumpRetainedLayerTree(layerManager,ss,false);print_stderr(ss);}#endif// Update the widget's opaque region information. This sets// glass boundaries on Windows. Also set up the window dragging region// and plugin clip regions and bounds.if((aFlags&PaintFrameFlags::PAINT_WIDGET_LAYERS)&&!(aFlags&PaintFrameFlags::PAINT_DOCUMENT_RELATIVE)){nsIWidget*widget=aFrame->GetNearestWidget();if(widget){nsRegionopaqueRegion;opaqueRegion.And(builder.GetWindowExcludeGlassRegion(),builder.GetWindowOpaqueRegion());widget->UpdateOpaqueRegion(LayoutDeviceIntRegion::FromUnknownRegion(opaqueRegion.ToNearestPixels(presContext->AppUnitsPerDevPixel())));widget->UpdateWindowDraggingRegion(builder.GetWindowDraggingRegion());}}if(builder.WillComputePluginGeometry()){// For single process compute and apply plugin geometry updates to plugin// windows, then request composition. For content processes skip eveything// except requesting composition. Geometry updates were calculated and// shipped to the chrome process in nsDisplayList when the layer// transaction completed.if(XRE_IsParentProcess()){rootPresContext->ComputePluginGeometryUpdates(aFrame,&builder,&list);// We're not going to get a WillPaintWindow event here if we didn't do// widget invalidation, so just apply the plugin geometry update here// instead. We could instead have the compositor send back an equivalent// to WillPaintWindow, but it should be close enough to now not to matter.if(layerManager&&!layerManager->NeedsWidgetInvalidation()){rootPresContext->ApplyPluginGeometryUpdates();}}// We told the compositor thread not to composite when it received the// transaction because we wanted to update plugins first. Schedule the// composite now.if(layerManager){layerManager->ScheduleComposite();}}// Flush the list so we don't trigger the IsEmpty-on-destruction assertionlist.DeleteAll();returnNS_OK;}/** * Uses a binary search for find where the cursor falls in the line of text * It also keeps track of the part of the string that has already been measured * so it doesn't have to keep measuring the same text over and over * * @param "aBaseWidth" contains the width in twips of the portion * of the text that has already been measured, and aBaseInx contains * the index of the text that has already been measured. * * @param aTextWidth returns the (in twips) the length of the text that falls * before the cursor aIndex contains the index of the text where the cursor falls */boolnsLayoutUtils::BinarySearchForPosition(DrawTarget*aDrawTarget,nsFontMetrics&aFontMetrics,constchar16_t*aText,int32_taBaseWidth,int32_taBaseInx,int32_taStartInx,int32_taEndInx,int32_taCursorPos,int32_t&aIndex,int32_t&aTextWidth){int32_trange=aEndInx-aStartInx;if((range==1)||(range==2&&NS_IS_HIGH_SURROGATE(aText[aStartInx]))){aIndex=aStartInx+aBaseInx;aTextWidth=nsLayoutUtils::AppUnitWidthOfString(aText,aIndex,aFontMetrics,aDrawTarget);returntrue;}int32_tinx=aStartInx+(range/2);// Make sure we don't leave a dangling low surrogateif(NS_IS_HIGH_SURROGATE(aText[inx-1]))inx++;int32_ttextWidth=nsLayoutUtils::AppUnitWidthOfString(aText,inx,aFontMetrics,aDrawTarget);int32_tfullWidth=aBaseWidth+textWidth;if(fullWidth==aCursorPos){aTextWidth=textWidth;aIndex=inx;returntrue;}elseif(aCursorPos<fullWidth){aTextWidth=aBaseWidth;if(BinarySearchForPosition(aDrawTarget,aFontMetrics,aText,aBaseWidth,aBaseInx,aStartInx,inx,aCursorPos,aIndex,aTextWidth)){returntrue;}}else{aTextWidth=fullWidth;if(BinarySearchForPosition(aDrawTarget,aFontMetrics,aText,aBaseWidth,aBaseInx,inx,aEndInx,aCursorPos,aIndex,aTextWidth)){returntrue;}}returnfalse;}staticvoidAddBoxesForFrame(nsIFrame*aFrame,nsLayoutUtils::BoxCallback*aCallback){nsIAtom*pseudoType=aFrame->StyleContext()->GetPseudo();if(pseudoType==nsCSSAnonBoxes::tableWrapper){AddBoxesForFrame(aFrame->PrincipalChildList().FirstChild(),aCallback);if(aCallback->mIncludeCaptionBoxForTable){nsIFrame*kid=aFrame->GetChildList(nsIFrame::kCaptionList).FirstChild();if(kid){AddBoxesForFrame(kid,aCallback);}}}elseif(pseudoType==nsCSSAnonBoxes::mozBlockInsideInlineWrapper||pseudoType==nsCSSAnonBoxes::mozMathMLAnonymousBlock||pseudoType==nsCSSAnonBoxes::mozXULAnonymousBlock){for(nsIFrame*kid:aFrame->PrincipalChildList()){AddBoxesForFrame(kid,aCallback);}}else{aCallback->AddBox(aFrame);}}voidnsLayoutUtils::GetAllInFlowBoxes(nsIFrame*aFrame,BoxCallback*aCallback){while(aFrame){AddBoxesForFrame(aFrame,aCallback);aFrame=nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);}}nsIFrame*nsLayoutUtils::GetFirstNonAnonymousFrame(nsIFrame*aFrame){while(aFrame){nsIAtom*pseudoType=aFrame->StyleContext()->GetPseudo();if(pseudoType==nsCSSAnonBoxes::tableWrapper){nsIFrame*f=GetFirstNonAnonymousFrame(aFrame->PrincipalChildList().FirstChild());if(f){returnf;}nsIFrame*kid=aFrame->GetChildList(nsIFrame::kCaptionList).FirstChild();if(kid){f=GetFirstNonAnonymousFrame(kid);if(f){returnf;}}}elseif(pseudoType==nsCSSAnonBoxes::mozBlockInsideInlineWrapper||pseudoType==nsCSSAnonBoxes::mozMathMLAnonymousBlock||pseudoType==nsCSSAnonBoxes::mozXULAnonymousBlock){for(nsIFrame*kid:aFrame->PrincipalChildList()){nsIFrame*f=GetFirstNonAnonymousFrame(kid);if(f){returnf;}}}else{returnaFrame;}aFrame=nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);}returnnullptr;}structBoxToRect:publicnsLayoutUtils::BoxCallback{nsIFrame*mRelativeTo;nsLayoutUtils::RectCallback*mCallback;uint32_tmFlags;BoxToRect(nsIFrame*aRelativeTo,nsLayoutUtils::RectCallback*aCallback,uint32_taFlags):mRelativeTo(aRelativeTo),mCallback(aCallback),mFlags(aFlags){}virtualvoidAddBox(nsIFrame*aFrame)override{nsRectr;nsIFrame*outer=nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(aFrame,&r);if(!outer){outer=aFrame;switch(mFlags&nsLayoutUtils::RECTS_WHICH_BOX_MASK){casensLayoutUtils::RECTS_USE_CONTENT_BOX:r=aFrame->GetContentRectRelativeToSelf();break;casensLayoutUtils::RECTS_USE_PADDING_BOX:r=aFrame->GetPaddingRectRelativeToSelf();break;casensLayoutUtils::RECTS_USE_MARGIN_BOX:r=aFrame->GetMarginRectRelativeToSelf();break;default:// Use the border boxr=aFrame->GetRectRelativeToSelf();}}if(mFlags&nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS){r=nsLayoutUtils::TransformFrameRectToAncestor(outer,r,mRelativeTo);}else{r+=outer->GetOffsetTo(mRelativeTo);}mCallback->AddRect(r);}};structMOZ_RAIIBoxToRectAndText:publicBoxToRect{Sequence<nsString>*mTextList;BoxToRectAndText(nsIFrame*aRelativeTo,nsLayoutUtils::RectCallback*aCallback,Sequence<nsString>*aTextList,uint32_taFlags):BoxToRect(aRelativeTo,aCallback,aFlags),mTextList(aTextList){}staticvoidAccumulateText(nsIFrame*aFrame,nsAString&aResult){MOZ_ASSERT(aFrame);// Get all the text in aFrame and child frames, while respecting// the content offsets in each of the nsTextFrames.if(aFrame->IsTextFrame()){nsTextFrame*textFrame=static_cast<nsTextFrame*>(aFrame);nsIFrame::RenderedTextrenderedText=textFrame->GetRenderedText(textFrame->GetContentOffset(),textFrame->GetContentOffset()+textFrame->GetContentLength(),nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);aResult.Append(renderedText.mString);}for(nsIFrame*child=aFrame->PrincipalChildList().FirstChild();child;child=child->GetNextSibling()){AccumulateText(child,aResult);}}virtualvoidAddBox(nsIFrame*aFrame)override{BoxToRect::AddBox(aFrame);if(mTextList){nsString*textForFrame=mTextList->AppendElement(fallible);if(textForFrame){AccumulateText(aFrame,*textForFrame);}}}};voidnsLayoutUtils::GetAllInFlowRects(nsIFrame*aFrame,nsIFrame*aRelativeTo,RectCallback*aCallback,uint32_taFlags){BoxToRectconverter(aRelativeTo,aCallback,aFlags);GetAllInFlowBoxes(aFrame,&converter);}voidnsLayoutUtils::GetAllInFlowRectsAndTexts(nsIFrame*aFrame,nsIFrame*aRelativeTo,RectCallback*aCallback,Sequence<nsString>*aTextList,uint32_taFlags){BoxToRectAndTextconverter(aRelativeTo,aCallback,aTextList,aFlags);GetAllInFlowBoxes(aFrame,&converter);}nsLayoutUtils::RectAccumulator::RectAccumulator():mSeenFirstRect(false){}voidnsLayoutUtils::RectAccumulator::AddRect(constnsRect&aRect){mResultRect.UnionRect(mResultRect,aRect);if(!mSeenFirstRect){mSeenFirstRect=true;mFirstRect=aRect;}}nsLayoutUtils::RectListBuilder::RectListBuilder(DOMRectList*aList):mRectList(aList){}voidnsLayoutUtils::RectListBuilder::AddRect(constnsRect&aRect){RefPtr<DOMRect>rect=newDOMRect(mRectList);rect->SetLayoutRect(aRect);mRectList->Append(rect);}nsIFrame*nsLayoutUtils::GetContainingBlockForClientRect(nsIFrame*aFrame){returnaFrame->PresContext()->PresShell()->GetRootFrame();}nsRectnsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame*aFrame,nsIFrame*aRelativeTo,uint32_taFlags){RectAccumulatoraccumulator;GetAllInFlowRects(aFrame,aRelativeTo,&accumulator,aFlags);returnaccumulator.mResultRect.IsEmpty()?accumulator.mFirstRect:accumulator.mResultRect;}nsRectnsLayoutUtils::GetTextShadowRectsUnion(constnsRect&aTextAndDecorationsRect,nsIFrame*aFrame,uint32_taFlags){constnsStyleText*textStyle=aFrame->StyleText();if(!textStyle->HasTextShadow())returnaTextAndDecorationsRect;nsRectresultRect=aTextAndDecorationsRect;int32_tA2D=aFrame->PresContext()->AppUnitsPerDevPixel();for(uint32_ti=0;i<textStyle->mTextShadow->Length();++i){nsCSSShadowItem*shadow=textStyle->mTextShadow->ShadowAt(i);nsMarginblur=nsContextBoxBlur::GetBlurRadiusMargin(shadow->mRadius,A2D);if((aFlags&EXCLUDE_BLUR_SHADOWS)&&blur!=nsMargin(0,0,0,0))continue;nsRecttmpRect(aTextAndDecorationsRect);tmpRect.MoveBy(nsPoint(shadow->mXOffset,shadow->mYOffset));tmpRect.Inflate(blur);resultRect.UnionRect(resultRect,tmpRect);}returnresultRect;}enumObjectDimensionType{eWidth,eHeight};staticnscoordComputeMissingDimension(constnsSize&aDefaultObjectSize,constnsSize&aIntrinsicRatio,constMaybe<nscoord>&aSpecifiedWidth,constMaybe<nscoord>&aSpecifiedHeight,ObjectDimensionTypeaDimensionToCompute){// The "default sizing algorithm" computes the missing dimension as follows:// (source: http://dev.w3.org/csswg/css-images-3/#default-sizing )// 1. "If the object has an intrinsic aspect ratio, the missing dimension of// the concrete object size is calculated using the intrinsic aspect// ratio and the present dimension."if(aIntrinsicRatio.width>0&&aIntrinsicRatio.height>0){// Fill in the missing dimension using the intrinsic aspect ratio.nscoordknownDimensionSize;floatratio;if(aDimensionToCompute==eWidth){knownDimensionSize=*aSpecifiedHeight;ratio=aIntrinsicRatio.width/aIntrinsicRatio.height;}else{knownDimensionSize=*aSpecifiedWidth;ratio=aIntrinsicRatio.height/aIntrinsicRatio.width;}returnNSCoordSaturatingNonnegativeMultiply(knownDimensionSize,ratio);}// 2. "Otherwise, if the missing dimension is present in the object’s// intrinsic dimensions, [...]"// NOTE: *Skipping* this case, because we already know it's not true -- we're// in this function because the missing dimension is *not* present in// the object's intrinsic dimensions.// 3. "Otherwise, the missing dimension of the concrete object size is taken// from the default object size. "return(aDimensionToCompute==eWidth)?aDefaultObjectSize.width:aDefaultObjectSize.height;}/* * This computes & returns the concrete object size of replaced content, if * that content were to be rendered with "object-fit: none". (Or, if the * element has neither an intrinsic height nor width, this method returns an * empty Maybe<> object.) * * As specced... * http://dev.w3.org/csswg/css-images-3/#valdef-object-fit-none * ..we use "the default sizing algorithm with no specified size, * and a default object size equal to the replaced element's used width and * height." * * The default sizing algorithm is described here: * http://dev.w3.org/csswg/css-images-3/#default-sizing * Quotes in the function-impl are taken from that ^ spec-text. * * Per its final bulleted section: since there's no specified size, * we run the default sizing algorithm using the object's intrinsic size in * place of the specified size. But if the object has neither an intrinsic * height nor an intrinsic width, then we instead return without populating our * outparam, and we let the caller figure out the size (using a contain * constraint). */staticMaybe<nsSize>MaybeComputeObjectFitNoneSize(constnsSize&aDefaultObjectSize,constIntrinsicSize&aIntrinsicSize,constnsSize&aIntrinsicRatio){// "If the object has an intrinsic height or width, its size is resolved as// if its intrinsic dimensions were given as the specified size."//// So, first we check if we have an intrinsic height and/or width:Maybe<nscoord>specifiedWidth;if(aIntrinsicSize.width.GetUnit()==eStyleUnit_Coord){specifiedWidth.emplace(aIntrinsicSize.width.GetCoordValue());}Maybe<nscoord>specifiedHeight;if(aIntrinsicSize.height.GetUnit()==eStyleUnit_Coord){specifiedHeight.emplace(aIntrinsicSize.height.GetCoordValue());}Maybe<nsSize>noneSize;// (the value we'll return)if(specifiedWidth||specifiedHeight){// We have at least one specified dimension; use whichever dimension is// specified, and compute the other one using our intrinsic ratio, or (if// no valid ratio) using the default object size.noneSize.emplace();noneSize->width=specifiedWidth?*specifiedWidth:ComputeMissingDimension(aDefaultObjectSize,aIntrinsicRatio,specifiedWidth,specifiedHeight,eWidth);noneSize->height=specifiedHeight?*specifiedHeight:ComputeMissingDimension(aDefaultObjectSize,aIntrinsicRatio,specifiedWidth,specifiedHeight,eHeight);}// [else:] "Otherwise [if there's neither an intrinsic height nor width], its// size is resolved as a contain constraint against the default object size."// We'll let our caller do that, to share code & avoid redundant// computations; so, we return w/out populating noneSize.returnnoneSize;}// Computes the concrete object size to render into, as described at// http://dev.w3.org/csswg/css-images-3/#concrete-size-resolutionstaticnsSizeComputeConcreteObjectSize(constnsSize&aConstraintSize,constIntrinsicSize&aIntrinsicSize,constnsSize&aIntrinsicRatio,uint8_taObjectFit){// Handle default behavior (filling the container) w/ fast early return.// (Also: if there's no valid intrinsic ratio, then we have the "fill"// behavior & just use the constraint size.)if(MOZ_LIKELY(aObjectFit==NS_STYLE_OBJECT_FIT_FILL)||aIntrinsicRatio.width==0||aIntrinsicRatio.height==0){returnaConstraintSize;}// The type of constraint to compute (cover/contain), if needed:Maybe<nsImageRenderer::FitType>fitType;Maybe<nsSize>noneSize;if(aObjectFit==NS_STYLE_OBJECT_FIT_NONE||aObjectFit==NS_STYLE_OBJECT_FIT_SCALE_DOWN){noneSize=MaybeComputeObjectFitNoneSize(aConstraintSize,aIntrinsicSize,aIntrinsicRatio);if(!noneSize||aObjectFit==NS_STYLE_OBJECT_FIT_SCALE_DOWN){// Need to compute a 'CONTAIN' constraint (either for the 'none' size// itself, or for comparison w/ the 'none' size to resolve 'scale-down'.)fitType.emplace(nsImageRenderer::CONTAIN);}}elseif(aObjectFit==NS_STYLE_OBJECT_FIT_COVER){fitType.emplace(nsImageRenderer::COVER);}elseif(aObjectFit==NS_STYLE_OBJECT_FIT_CONTAIN){fitType.emplace(nsImageRenderer::CONTAIN);}Maybe<nsSize>constrainedSize;if(fitType){constrainedSize.emplace(nsImageRenderer::ComputeConstrainedSize(aConstraintSize,aIntrinsicRatio,*fitType));}// Now, we should have all the sizing information that we need.switch(aObjectFit){// skipping NS_STYLE_OBJECT_FIT_FILL; we handled it w/ early-return.caseNS_STYLE_OBJECT_FIT_CONTAIN:caseNS_STYLE_OBJECT_FIT_COVER:MOZ_ASSERT(constrainedSize);return*constrainedSize;caseNS_STYLE_OBJECT_FIT_NONE:if(noneSize){return*noneSize;}MOZ_ASSERT(constrainedSize);return*constrainedSize;caseNS_STYLE_OBJECT_FIT_SCALE_DOWN:MOZ_ASSERT(constrainedSize);if(noneSize){constrainedSize->width=std::min(constrainedSize->width,noneSize->width);constrainedSize->height=std::min(constrainedSize->height,noneSize->height);}return*constrainedSize;default:MOZ_ASSERT_UNREACHABLE("Unexpected enum value for 'object-fit'");returnaConstraintSize;// fall back to (default) 'fill' behavior}}// (Helper for HasInitialObjectFitAndPosition, to check// each "object-position" coord.)staticboolIsCoord50Pct(constmozilla::Position::Coord&aCoord){return(aCoord.mLength==0&&aCoord.mHasPercent&&aCoord.mPercent==0.5f);}// Indicates whether the given nsStylePosition has the initial values// for the "object-fit" and "object-position" properties.staticboolHasInitialObjectFitAndPosition(constnsStylePosition*aStylePos){constmozilla::Position&objectPos=aStylePos->mObjectPosition;returnaStylePos->mObjectFit==NS_STYLE_OBJECT_FIT_FILL&&IsCoord50Pct(objectPos.mXPosition)&&IsCoord50Pct(objectPos.mYPosition);}/* static */nsRectnsLayoutUtils::ComputeObjectDestRect(constnsRect&aConstraintRect,constIntrinsicSize&aIntrinsicSize,constnsSize&aIntrinsicRatio,constnsStylePosition*aStylePos,nsPoint*aAnchorPoint){// Step 1: Figure out our "concrete object size"// (the size of the region we'll actually draw our image's pixels into).nsSizeconcreteObjectSize=ComputeConcreteObjectSize(aConstraintRect.Size(),aIntrinsicSize,aIntrinsicRatio,aStylePos->mObjectFit);// Step 2: Figure out how to align that region in the element's content-box.nsPointimageTopLeftPt,imageAnchorPt;nsImageRenderer::ComputeObjectAnchorPoint(aStylePos->mObjectPosition,aConstraintRect.Size(),concreteObjectSize,&imageTopLeftPt,&imageAnchorPt);// Right now, we're with respect to aConstraintRect's top-left point. We add// that point here, to convert to the same broader coordinate space that// aConstraintRect is in.imageTopLeftPt+=aConstraintRect.TopLeft();imageAnchorPt+=aConstraintRect.TopLeft();if(aAnchorPoint){// Special-case: if our "object-fit" and "object-position" properties have// their default values ("object-fit: fill; object-position:50% 50%"), then// we'll override the calculated imageAnchorPt, and instead use the// object's top-left corner.//// This special case is partly for backwards compatibility (since// traditionally we've pixel-aligned the top-left corner of e.g. <img>// elements), and partly because ComputeSnappedDrawingParameters produces// less error if the anchor point is at the top-left corner. So, all other// things being equal, we prefer that code path with less error.if(HasInitialObjectFitAndPosition(aStylePos)){*aAnchorPoint=imageTopLeftPt;}else{*aAnchorPoint=imageAnchorPt;}}returnnsRect(imageTopLeftPt,concreteObjectSize);}already_AddRefed<nsFontMetrics>nsLayoutUtils::GetFontMetricsForFrame(constnsIFrame*aFrame,floataInflation){nsStyleContext*styleContext=aFrame->StyleContext();uint8_tvariantWidth=NS_FONT_VARIANT_WIDTH_NORMAL;if(styleContext->IsTextCombined()){MOZ_ASSERT(aFrame->IsTextFrame());autotextFrame=static_cast<constnsTextFrame*>(aFrame);autoclusters=textFrame->CountGraphemeClusters();if(clusters==2){variantWidth=NS_FONT_VARIANT_WIDTH_HALF;}elseif(clusters==3){variantWidth=NS_FONT_VARIANT_WIDTH_THIRD;}elseif(clusters==4){variantWidth=NS_FONT_VARIANT_WIDTH_QUARTER;}}returnGetFontMetricsForStyleContext(styleContext,aInflation,variantWidth);}already_AddRefed<nsFontMetrics>nsLayoutUtils::GetFontMetricsForStyleContext(nsStyleContext*aStyleContext,floataInflation,uint8_taVariantWidth){nsPresContext*pc=aStyleContext->PresContext();WritingModewm(aStyleContext);constnsStyleFont*styleFont=aStyleContext->StyleFont();nsFontMetrics::Paramsparams;params.language=styleFont->mLanguage;params.explicitLanguage=styleFont->mExplicitLanguage;params.orientation=wm.IsVertical()&&!wm.IsSideways()?gfxFont::eVertical:gfxFont::eHorizontal;// pass the user font set object into the device context to// pass along to CreateFontGroupparams.userFontSet=pc->GetUserFontSet();params.textPerf=pc->GetTextPerfMetrics();// When aInflation is 1.0 and we don't require width variant, avoid// making a local copy of the nsFont.// This also avoids running font.size through floats when it is large,// which would be lossy. Fortunately, in such cases, aInflation is// guaranteed to be 1.0f.if(aInflation==1.0f&&aVariantWidth==NS_FONT_VARIANT_WIDTH_NORMAL){returnpc->DeviceContext()->GetMetricsFor(styleFont->mFont,params);}nsFontfont=styleFont->mFont;font.size=NSToCoordRound(font.size*aInflation);font.variantWidth=aVariantWidth;returnpc->DeviceContext()->GetMetricsFor(font,params);}nsIFrame*nsLayoutUtils::FindChildContainingDescendant(nsIFrame*aParent,nsIFrame*aDescendantFrame){nsIFrame*result=aDescendantFrame;while(result){nsIFrame*parent=result->GetParent();if(parent==aParent){break;}// The frame is not an immediate child of aParent so walk up another levelresult=parent;}returnresult;}nsBlockFrame*nsLayoutUtils::GetAsBlock(nsIFrame*aFrame){nsBlockFrame*block=do_QueryFrame(aFrame);returnblock;}nsBlockFrame*nsLayoutUtils::FindNearestBlockAncestor(nsIFrame*aFrame){nsIFrame*nextAncestor;for(nextAncestor=aFrame->GetParent();nextAncestor;nextAncestor=nextAncestor->GetParent()){nsBlockFrame*block=GetAsBlock(nextAncestor);if(block)returnblock;}returnnullptr;}nsIFrame*nsLayoutUtils::GetNonGeneratedAncestor(nsIFrame*aFrame){if(!(aFrame->GetStateBits()&NS_FRAME_GENERATED_CONTENT))returnaFrame;nsIFrame*f=aFrame;do{f=GetParentOrPlaceholderFor(f);}while(f->GetStateBits()&NS_FRAME_GENERATED_CONTENT);returnf;}nsIFrame*nsLayoutUtils::GetParentOrPlaceholderFor(nsIFrame*aFrame){if((aFrame->GetStateBits()&NS_FRAME_OUT_OF_FLOW)&&!aFrame->GetPrevInFlow()){returnaFrame->GetProperty(nsIFrame::PlaceholderFrameProperty());}returnaFrame->GetParent();}nsIFrame*nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(nsIFrame*aFrame){nsIFrame*f=GetParentOrPlaceholderFor(aFrame);if(f)returnf;returnGetCrossDocParentFrame(aFrame);}nsIFrame*nsLayoutUtils::GetNextContinuationOrIBSplitSibling(nsIFrame*aFrame){nsIFrame*result=aFrame->GetNextContinuation();if(result)returnresult;if((aFrame->GetStateBits()&NS_FRAME_PART_OF_IBSPLIT)!=0){// We only store the ib-split sibling annotation with the first// frame in the continuation chain. Walk back to find that frame now.aFrame=aFrame->FirstContinuation();returnaFrame->GetProperty(nsIFrame::IBSplitSibling());}returnnullptr;}nsIFrame*nsLayoutUtils::FirstContinuationOrIBSplitSibling(constnsIFrame*aFrame){nsIFrame*result=aFrame->FirstContinuation();if(result->GetStateBits()&NS_FRAME_PART_OF_IBSPLIT){while(auto*f=result->GetProperty(nsIFrame::IBSplitPrevSibling())){result=f;}}returnresult;}nsIFrame*nsLayoutUtils::LastContinuationOrIBSplitSibling(constnsIFrame*aFrame){nsIFrame*result=aFrame->FirstContinuation();if(result->GetStateBits()&NS_FRAME_PART_OF_IBSPLIT){while(auto*f=result->GetProperty(nsIFrame::IBSplitSibling())){result=f;}}returnresult->LastContinuation();}boolnsLayoutUtils::IsFirstContinuationOrIBSplitSibling(nsIFrame*aFrame){if(aFrame->GetPrevContinuation()){returnfalse;}if((aFrame->GetStateBits()&NS_FRAME_PART_OF_IBSPLIT)&&aFrame->GetProperty(nsIFrame::IBSplitPrevSibling())){returnfalse;}returntrue;}boolnsLayoutUtils::IsViewportScrollbarFrame(nsIFrame*aFrame){if(!aFrame)returnfalse;nsIFrame*rootScrollFrame=aFrame->PresContext()->PresShell()->GetRootScrollFrame();if(!rootScrollFrame)returnfalse;nsIScrollableFrame*rootScrollableFrame=do_QueryFrame(rootScrollFrame);NS_ASSERTION(rootScrollableFrame,"The root scorollable frame is null");if(!IsProperAncestorFrame(rootScrollFrame,aFrame))returnfalse;nsIFrame*rootScrolledFrame=rootScrollableFrame->GetScrolledFrame();return!(rootScrolledFrame==aFrame||IsProperAncestorFrame(rootScrolledFrame,aFrame));}// Use only for widths/heights (or their min/max), since it clamps// negative calc() results to 0.staticboolGetAbsoluteCoord(constnsStyleCoord&aStyle,nscoord&aResult){if(aStyle.IsCalcUnit()){if(aStyle.CalcHasPercent()){returnfalse;}// If it has no percents, we can pass 0 for the percentage basis.aResult=nsRuleNode::ComputeComputedCalc(aStyle,0);if(aResult<0)aResult=0;returntrue;}if(eStyleUnit_Coord!=aStyle.GetUnit())returnfalse;aResult=aStyle.GetCoordValue();NS_ASSERTION(aResult>=0,"negative widths not allowed");returntrue;}staticnscoordGetBSizeTakenByBoxSizing(StyleBoxSizingaBoxSizing,nsIFrame*aFrame,boolaHorizontalAxis,boolaIgnorePadding);// Only call on style coords for which GetAbsoluteCoord returned false.staticboolGetPercentBSize(constnsStyleCoord&aStyle,nsIFrame*aFrame,boolaHorizontalAxis,nscoord&aResult){if(eStyleUnit_Percent!=aStyle.GetUnit()&&!aStyle.IsCalcUnit())returnfalse;MOZ_ASSERT(!aStyle.IsCalcUnit()||aStyle.CalcHasPercent(),"GetAbsoluteCoord should have handled this");// During reflow, nsHTMLScrollFrame::ReflowScrolledFrame uses// SetComputedHeight on the reflow state for its child to propagate its// computed height to the scrolled content. So here we skip to the scroll// frame that contains this scrolled content in order to get the same// behavior as layout when computing percentage heights.nsIFrame*f=aFrame->GetContainingBlock(nsIFrame::SKIP_SCROLLED_FRAME);if(!f){NS_NOTREACHED("top of frame tree not a containing block");returnfalse;}WritingModewm=f->GetWritingMode();constnsStylePosition*pos=f->StylePosition();constnsStyleCoord&bSizeCoord=pos->BSize(wm);nscoordh;if(!GetAbsoluteCoord(bSizeCoord,h)&&!GetPercentBSize(bSizeCoord,f,aHorizontalAxis,h)){NS_ASSERTION(bSizeCoord.GetUnit()==eStyleUnit_Auto||bSizeCoord.HasPercent(),"unknown block-size unit");LayoutFrameTypefType=f->Type();if(fType!=LayoutFrameType::Viewport&&fType!=LayoutFrameType::Canvas&&fType!=LayoutFrameType::PageContent){// There's no basis for the percentage height, so it acts like auto.// Should we consider a max-height < min-height pair a basis for// percentage heights? The spec is somewhat unclear, and not doing// so is simpler and avoids troubling discontinuities in behavior,// so I'll choose not to. -LDBreturnfalse;}NS_ASSERTION(bSizeCoord.GetUnit()==eStyleUnit_Auto,"Unexpected block-size unit for viewport or canvas or page-content");// For the viewport, canvas, and page-content kids, the percentage// basis is just the parent block-size.h=f->BSize(wm);if(h==NS_UNCONSTRAINEDSIZE){// We don't have a percentage basis after allreturnfalse;}}constnsStyleCoord&maxBSizeCoord=pos->MaxBSize(wm);nscoordmaxh;if(GetAbsoluteCoord(maxBSizeCoord,maxh)||GetPercentBSize(maxBSizeCoord,f,aHorizontalAxis,maxh)){if(maxh<h)h=maxh;}else{NS_ASSERTION(maxBSizeCoord.GetUnit()==eStyleUnit_None||maxBSizeCoord.HasPercent(),"unknown max block-size unit");}constnsStyleCoord&minBSizeCoord=pos->MinBSize(wm);nscoordminh;if(GetAbsoluteCoord(minBSizeCoord,minh)||GetPercentBSize(minBSizeCoord,f,aHorizontalAxis,minh)){if(minh>h)h=minh;}else{NS_ASSERTION(minBSizeCoord.HasPercent()||minBSizeCoord.GetUnit()==eStyleUnit_Auto,"unknown min block-size unit");}// Now adjust h for box-sizing styles on the parent. We never ignore padding// here. That could conceivably cause some problems with fieldsets (which are// the one place that wants to ignore padding), but solving that here without// hardcoding a check for f being a fieldset-content frame is a bit of a pain.nscoordbSizeTakenByBoxSizing=GetBSizeTakenByBoxSizing(pos->mBoxSizing,f,aHorizontalAxis,false);h=std::max(0,h-bSizeTakenByBoxSizing);if(aStyle.IsCalcUnit()){aResult=std::max(nsRuleNode::ComputeComputedCalc(aStyle,h),0);returntrue;}aResult=NSToCoordRound(aStyle.GetPercentValue()*h);returntrue;}// Return true if aStyle can be resolved to a definite value and if so// return that value in aResult.staticboolGetDefiniteSize(constnsStyleCoord&aStyle,nsIFrame*aFrame,boolaIsInlineAxis,constMaybe<LogicalSize>&aPercentageBasis,nscoord*aResult){switch(aStyle.GetUnit()){caseeStyleUnit_Coord:*aResult=aStyle.GetCoordValue();returntrue;caseeStyleUnit_Percent:{if(aPercentageBasis.isNothing()){returnfalse;}autowm=aFrame->GetWritingMode();nscoordpb=aIsInlineAxis?aPercentageBasis.value().ISize(wm):aPercentageBasis.value().BSize(wm);if(pb!=NS_UNCONSTRAINEDSIZE){nscoordp=NSToCoordFloorClamped(pb*aStyle.GetPercentValue());*aResult=std::max(nscoord(0),p);returntrue;}returnfalse;}caseeStyleUnit_Calc:{nsStyleCoord::Calc*calc=aStyle.GetCalcValue();if(calc->mPercent!=0.0f){if(aPercentageBasis.isNothing()){returnfalse;}autowm=aFrame->GetWritingMode();nscoordpb=aIsInlineAxis?aPercentageBasis.value().ISize(wm):aPercentageBasis.value().BSize(wm);if(pb==NS_UNCONSTRAINEDSIZE){// XXXmats given that we're calculating an intrinsic size here,// maybe we should back-compute the calc-size using AddPercents?returnfalse;}*aResult=std::max(0,calc->mLength+NSToCoordFloorClamped(pb*calc->mPercent));}else{*aResult=std::max(0,calc->mLength);}returntrue;}default:returnfalse;}}//// NOTE: this function will be replaced by GetDefiniteSizeTakenByBoxSizing (bug 1363918).// Please do not add new uses of this function.//// Get the amount of vertical space taken out of aFrame's content area due to// its borders and paddings given the box-sizing value in aBoxSizing. We don't// get aBoxSizing from the frame because some callers want to compute this for// specific box-sizing values. aHorizontalAxis is true if our inline direction// is horisontal and our block direction is vertical. aIgnorePadding is true if// padding should be ignored.staticnscoordGetBSizeTakenByBoxSizing(StyleBoxSizingaBoxSizing,nsIFrame*aFrame,boolaHorizontalAxis,boolaIgnorePadding){nscoordbSizeTakenByBoxSizing=0;if(aBoxSizing==StyleBoxSizing::Border){constnsStyleBorder*styleBorder=aFrame->StyleBorder();bSizeTakenByBoxSizing+=aHorizontalAxis?styleBorder->GetComputedBorder().TopBottom():styleBorder->GetComputedBorder().LeftRight();if(!aIgnorePadding){constnsStyleSides&stylePadding=aFrame->StylePadding()->mPadding;constnsStyleCoord&paddingStart=stylePadding.Get(aHorizontalAxis?eSideTop:eSideLeft);constnsStyleCoord&paddingEnd=stylePadding.Get(aHorizontalAxis?eSideBottom:eSideRight);nscoordpad;// XXXbz Calling GetPercentBSize on padding values looks bogus, since// percent padding is always a percentage of the inline-size of the// containing block. We should perhaps just treat non-absolute paddings// here as 0 instead, except that in some cases the width may in fact be// known. See bug 1231059.if(GetAbsoluteCoord(paddingStart,pad)||GetPercentBSize(paddingStart,aFrame,aHorizontalAxis,pad)){bSizeTakenByBoxSizing+=pad;}if(GetAbsoluteCoord(paddingEnd,pad)||GetPercentBSize(paddingEnd,aFrame,aHorizontalAxis,pad)){bSizeTakenByBoxSizing+=pad;}}}returnbSizeTakenByBoxSizing;}// Get the amount of space taken out of aFrame's content area due to its// borders and paddings given the box-sizing value in aBoxSizing. We don't// get aBoxSizing from the frame because some callers want to compute this for// specific box-sizing values.// aIsInlineAxis is true if we're computing for aFrame's inline axis.// aIgnorePadding is true if padding should be ignored.staticnscoordGetDefiniteSizeTakenByBoxSizing(StyleBoxSizingaBoxSizing,nsIFrame*aFrame,boolaIsInlineAxis,boolaIgnorePadding,constMaybe<LogicalSize>&aPercentageBasis){nscoordsizeTakenByBoxSizing=0;if(MOZ_UNLIKELY(aBoxSizing==StyleBoxSizing::Border)){constboolisHorizontalAxis=aIsInlineAxis==!aFrame->GetWritingMode().IsVertical();constnsStyleBorder*styleBorder=aFrame->StyleBorder();sizeTakenByBoxSizing=isHorizontalAxis?styleBorder->GetComputedBorder().LeftRight():styleBorder->GetComputedBorder().TopBottom();if(!aIgnorePadding){constnsStyleSides&stylePadding=aFrame->StylePadding()->mPadding;constnsStyleCoord&pStart=stylePadding.Get(isHorizontalAxis?eSideLeft:eSideTop);constnsStyleCoord&pEnd=stylePadding.Get(isHorizontalAxis?eSideRight:eSideBottom);nscoordpad;// XXXbz Calling GetPercentBSize on padding values looks bogus, since// percent padding is always a percentage of the inline-size of the// containing block. We should perhaps just treat non-absolute paddings// here as 0 instead, except that in some cases the width may in fact be// known. See bug 1231059.if(GetDefiniteSize(pStart,aFrame,aIsInlineAxis,aPercentageBasis,&pad)||(aPercentageBasis.isNothing()&&GetPercentBSize(pStart,aFrame,isHorizontalAxis,pad))){sizeTakenByBoxSizing+=pad;}if(GetDefiniteSize(pEnd,aFrame,aIsInlineAxis,aPercentageBasis,&pad)||(aPercentageBasis.isNothing()&&GetPercentBSize(pEnd,aFrame,isHorizontalAxis,pad))){sizeTakenByBoxSizing+=pad;}}}returnsizeTakenByBoxSizing;}// Handles only -moz-max-content and -moz-min-content, and// -moz-fit-content for min-width and max-width, since the others// (-moz-fit-content for width, and -moz-available) have no effect on// intrinsic widths.enumeWidthProperty{PROP_WIDTH,PROP_MAX_WIDTH,PROP_MIN_WIDTH};staticboolGetIntrinsicCoord(constnsStyleCoord&aStyle,gfxContext*aRenderingContext,nsIFrame*aFrame,eWidthPropertyaProperty,nscoord&aResult){NS_PRECONDITION(aProperty==PROP_WIDTH||aProperty==PROP_MAX_WIDTH||aProperty==PROP_MIN_WIDTH,"unexpected property");if(aStyle.GetUnit()!=eStyleUnit_Enumerated)returnfalse;int32_tval=aStyle.GetIntValue();NS_ASSERTION(val==NS_STYLE_WIDTH_MAX_CONTENT||val==NS_STYLE_WIDTH_MIN_CONTENT||val==NS_STYLE_WIDTH_FIT_CONTENT||val==NS_STYLE_WIDTH_AVAILABLE,"unexpected enumerated value for width property");if(val==NS_STYLE_WIDTH_AVAILABLE)returnfalse;if(val==NS_STYLE_WIDTH_FIT_CONTENT){if(aProperty==PROP_WIDTH)returnfalse;// handle like 'width: auto'if(aProperty==PROP_MAX_WIDTH)// constrain large 'width' values down to -moz-max-contentval=NS_STYLE_WIDTH_MAX_CONTENT;else// constrain small 'width' or 'max-width' values up to -moz-min-contentval=NS_STYLE_WIDTH_MIN_CONTENT;}NS_ASSERTION(val==NS_STYLE_WIDTH_MAX_CONTENT||val==NS_STYLE_WIDTH_MIN_CONTENT,"should have reduced everything remaining to one of these");// If aFrame is a container for font size inflation, then shrink// wrapping inside of it should not apply font size inflation.AutoMaybeDisableFontInflationan(aFrame);if(val==NS_STYLE_WIDTH_MAX_CONTENT)aResult=aFrame->GetPrefISize(aRenderingContext);elseaResult=aFrame->GetMinISize(aRenderingContext);returntrue;}#undef DEBUG_INTRINSIC_WIDTH#ifdef DEBUG_INTRINSIC_WIDTHstaticint32_tgNoiseIndent=0;#endif// Return true for form controls whose minimum intrinsic inline-size// shrinks to 0 when they have a percentage inline-size (but not// percentage max-inline-size). (Proper replaced elements, whose// intrinsic minimium inline-size shrinks to 0 for both percentage// inline-size and percentage max-inline-size, are handled elsewhere.)inlinestaticboolFormControlShrinksForPercentISize(nsIFrame*aFrame){if(!aFrame->IsFrameOfType(nsIFrame::eReplaced)){// Quick test to reject most frames.returnfalse;}LayoutFrameTypefType=aFrame->Type();if(fType==LayoutFrameType::Meter||fType==LayoutFrameType::Progress){// progress and meter do have this shrinking behavior// FIXME: Maybe these should be nsIFormControlFrame?returntrue;}if(!static_cast<nsIFormControlFrame*>(do_QueryFrame(aFrame))){// Not a form control. This includes fieldsets, which do not// shrink.returnfalse;}if(fType==LayoutFrameType::GfxButtonControl||fType==LayoutFrameType::HTMLButtonControl){// Buttons don't have this shrinking behavior. (Note that color// inputs do, even though they inherit from button, so we can't use// do_QueryFrame here.)returnfalse;}returntrue;}/** * Add aOffsets which describes what to add on outside of the content box * aContentSize (controlled by 'box-sizing') and apply min/max properties. * We have to account for these properties after getting all the offsets * (margin, border, padding) because percentages do not operate linearly. * Doing this is ok because although percentages aren't handled linearly, * they are handled monotonically. * * @param aContentSize the content size calculated so far (@see IntrinsicForContainer) * @param aContentMinSize ditto min content size * @param aStyleSize a 'width' or 'height' property value * @param aFixedMinSize if aStyleMinSize is a definite size then this points to * the value, otherwise nullptr * @param aStyleMinSize a 'min-width' or 'min-height' property value * @param aFixedMaxSize if aStyleMaxSize is a definite size then this points to * the value, otherwise nullptr * @param aStyleMaxSize a 'max-width' or 'max-height' property value * @param aFlags same as for IntrinsicForContainer * @param aContainerWM the container's WM */staticnscoordAddIntrinsicSizeOffset(gfxContext*aRenderingContext,nsIFrame*aFrame,constnsIFrame::IntrinsicISizeOffsetData&aOffsets,nsLayoutUtils::IntrinsicISizeTypeaType,StyleBoxSizingaBoxSizing,nscoordaContentSize,nscoordaContentMinSize,constnsStyleCoord&aStyleSize,constnscoord*aFixedMinSize,constnsStyleCoord&aStyleMinSize,constnscoord*aFixedMaxSize,constnsStyleCoord&aStyleMaxSize,uint32_taFlags,PhysicalAxisaAxis){nscoordresult=aContentSize;nscoordmin=aContentMinSize;nscoordcoordOutsideSize=0;floatpctOutsideSize=0;floatpctTotal=0.0f;if(!(aFlags&nsLayoutUtils::IGNORE_PADDING)){coordOutsideSize+=aOffsets.hPadding;pctOutsideSize+=aOffsets.hPctPadding;}coordOutsideSize+=aOffsets.hBorder;if(aBoxSizing==StyleBoxSizing::Border){min+=coordOutsideSize;result=NSCoordSaturatingAdd(result,coordOutsideSize);pctTotal+=pctOutsideSize;coordOutsideSize=0;pctOutsideSize=0.0f;}coordOutsideSize+=aOffsets.hMargin;pctOutsideSize+=aOffsets.hPctMargin;min+=coordOutsideSize;result=NSCoordSaturatingAdd(result,coordOutsideSize);pctTotal+=pctOutsideSize;constboolshouldAddPercent=aType==nsLayoutUtils::PREF_ISIZE||(aFlags&nsLayoutUtils::ADD_PERCENTS);nscoordsize;if(aType==nsLayoutUtils::MIN_ISIZE&&(((aStyleSize.HasPercent()||aStyleMaxSize.HasPercent())&&aFrame->IsFrameOfType(nsIFrame::eReplacedSizing))||(aStyleSize.HasPercent()&&FormControlShrinksForPercentISize(aFrame)))){// A percentage width or max-width on replaced elements means they// can shrink to 0.// This is also true for percentage widths (but not max-widths) on// text inputs.// Note that if this is max-width, this overrides the fixed-width// rule in the next condition.result=0;// let |min| handle padding/border/margin}elseif(GetAbsoluteCoord(aStyleSize,size)||GetIntrinsicCoord(aStyleSize,aRenderingContext,aFrame,PROP_WIDTH,size)){result=size+coordOutsideSize;if(shouldAddPercent){result=nsLayoutUtils::AddPercents(result,pctOutsideSize);}}else{// NOTE: We could really do a lot better for percents and for some// cases of calc() containing percent (certainly including any where// the coefficient on the percent is positive and there are no max()// expressions). However, doing better for percents wouldn't be// backwards compatible.if(shouldAddPercent){result=nsLayoutUtils::AddPercents(result,pctTotal);}}nscoordmaxSize=aFixedMaxSize?*aFixedMaxSize:0;if(aFixedMaxSize||GetIntrinsicCoord(aStyleMaxSize,aRenderingContext,aFrame,PROP_MAX_WIDTH,maxSize)){maxSize+=coordOutsideSize;if(shouldAddPercent){maxSize=nsLayoutUtils::AddPercents(maxSize,pctOutsideSize);}if(result>maxSize){result=maxSize;}}nscoordminSize=aFixedMinSize?*aFixedMinSize:0;if(aFixedMinSize||GetIntrinsicCoord(aStyleMinSize,aRenderingContext,aFrame,PROP_MIN_WIDTH,minSize)){minSize+=coordOutsideSize;if(shouldAddPercent){minSize=nsLayoutUtils::AddPercents(minSize,pctOutsideSize);}if(result<minSize){result=minSize;}}if(shouldAddPercent){min=nsLayoutUtils::AddPercents(min,pctTotal);}if(result<min){result=min;}constnsStyleDisplay*disp=aFrame->StyleDisplay();if(aFrame->IsThemed(disp)){LayoutDeviceIntSizedevSize;boolcanOverride=true;nsPresContext*pc=aFrame->PresContext();pc->GetTheme()->GetMinimumWidgetSize(pc,aFrame,disp->mAppearance,&devSize,&canOverride);nscoordthemeSize=pc->DevPixelsToAppUnits(aAxis==eAxisVertical?devSize.height:devSize.width);// GetMinimumWidgetSize() returns a border-box width.themeSize+=aOffsets.hMargin;if(shouldAddPercent){themeSize=nsLayoutUtils::AddPercents(themeSize,aOffsets.hPctMargin);}if(themeSize>result||!canOverride){result=themeSize;}}returnresult;}staticvoidAddStateBitToAncestors(nsIFrame*aFrame,nsFrameStateaBit){for(nsIFrame*f=aFrame;f;f=f->GetParent()){if(f->HasAnyStateBits(aBit)){break;}f->AddStateBits(aBit);}}/* static */nscoordnsLayoutUtils::IntrinsicForAxis(PhysicalAxisaAxis,gfxContext*aRenderingContext,nsIFrame*aFrame,IntrinsicISizeTypeaType,constMaybe<LogicalSize>&aPercentageBasis,uint32_taFlags,nscoordaMarginBoxMinSizeClamp){NS_PRECONDITION(aFrame,"null frame");NS_PRECONDITION(aFrame->GetParent(),"IntrinsicForAxis called on frame not in tree");NS_PRECONDITION(aType==MIN_ISIZE||aType==PREF_ISIZE,"bad type");MOZ_ASSERT(aFrame->GetParent()->Type()!=LayoutFrameType::GridContainer||aPercentageBasis.isSome(),"grid layout should always pass a percentage basis");constboolhorizontalAxis=MOZ_LIKELY(aAxis==eAxisHorizontal);#ifdef DEBUG_INTRINSIC_WIDTHnsFrame::IndentBy(stderr,gNoiseIndent);static_cast<nsFrame*>(aFrame)->ListTag(stderr);printf_stderr(" %s %s intrinsic size for container:\n",aType==MIN_ISIZE?"min":"pref",horizontalAxis?"horizontal":"vertical");#endif// If aFrame is a container for font size inflation, then shrink// wrapping inside of it should not apply font size inflation.AutoMaybeDisableFontInflationan(aFrame);// We want the size this frame will contribute to the parent's inline-size,// so we work in the parent's writing mode; but if aFrame is orthogonal to// its parent, we'll need to look at its BSize instead of min/pref-ISize.constnsStylePosition*stylePos=aFrame->StylePosition();StyleBoxSizingboxSizing=stylePos->mBoxSizing;constnsStyleCoord&styleMinISize=horizontalAxis?stylePos->mMinWidth:stylePos->mMinHeight;constnsStyleCoord&styleISize=(aFlags&MIN_INTRINSIC_ISIZE)?styleMinISize:(horizontalAxis?stylePos->mWidth:stylePos->mHeight);MOZ_ASSERT(!(aFlags&MIN_INTRINSIC_ISIZE)||styleISize.GetUnit()==eStyleUnit_Auto||styleISize.GetUnit()==eStyleUnit_Enumerated,"should only use MIN_INTRINSIC_ISIZE for intrinsic values");constnsStyleCoord&styleMaxISize=horizontalAxis?stylePos->mMaxWidth:stylePos->mMaxHeight;// We build up two values starting with the content box, and then// adding padding, border and margin. The result is normally// |result|. Then, when we handle 'width', 'min-width', and// 'max-width', we use the results we've been building in |min| as a// minimum, overriding 'min-width'. This ensures two things:// * that we don't let a value of 'box-sizing' specifying a width// smaller than the padding/border inside the box-sizing box give// a content width less than zero// * that we prevent tables from becoming smaller than their// intrinsic minimum widthnscoordresult=0,min=0;nscoordmaxISize;boolhaveFixedMaxISize=GetAbsoluteCoord(styleMaxISize,maxISize);nscoordminISize;// Treat "min-width: auto" as 0.boolhaveFixedMinISize;if(eStyleUnit_Auto==styleMinISize.GetUnit()){// NOTE: Technically, "auto" is supposed to behave like "min-content" on// flex items. However, we don't need to worry about that here, because// flex items' min-sizes are intentionally ignored until the flex// container explicitly considers them during space distribution.minISize=0;haveFixedMinISize=true;}else{haveFixedMinISize=GetAbsoluteCoord(styleMinISize,minISize);}PhysicalAxisourInlineAxis=aFrame->GetWritingMode().PhysicalAxis(eLogicalAxisInline);constboolisInlineAxis=aAxis==ourInlineAxis;// If we have a specified width (or a specified 'min-width' greater// than the specified 'max-width', which works out to the same thing),// don't even bother getting the frame's intrinsic width, because in// this case GetAbsoluteCoord(styleISize, w) will always succeed, so// we'll never need the intrinsic dimensions.if(styleISize.GetUnit()==eStyleUnit_Enumerated&&(styleISize.GetIntValue()==NS_STYLE_WIDTH_MAX_CONTENT||styleISize.GetIntValue()==NS_STYLE_WIDTH_MIN_CONTENT)){// -moz-fit-content and -moz-available enumerated widths compute intrinsic// widths just like auto.// For -moz-max-content and -moz-min-content, we handle them like// specified widths, but ignore box-sizing.boxSizing=StyleBoxSizing::Content;if(aMarginBoxMinSizeClamp!=NS_MAXSIZE&&styleISize.GetIntValue()==NS_STYLE_WIDTH_MIN_CONTENT){// We need |result| to be the 'min-content size' for the clamping below.result=aFrame->GetMinISize(aRenderingContext);}}elseif(!styleISize.ConvertsToLength()&&!(haveFixedMinISize&&haveFixedMaxISize&&maxISize<=minISize)){#ifdef DEBUG_INTRINSIC_WIDTH++gNoiseIndent;#endifif(aType!=MIN_ISIZE){// At this point, |styleISize| is auto/-moz-fit-content/-moz-available or// has a percentage. The intrinisic size for those under a max-content// constraint is the max-content contribution which we shouldn't clamp.aMarginBoxMinSizeClamp=NS_MAXSIZE;}if(MOZ_UNLIKELY(!isInlineAxis)){IntrinsicSizeintrinsicSize=aFrame->GetIntrinsicSize();constnsStyleCoordintrinsicBCoord=horizontalAxis?intrinsicSize.width:intrinsicSize.height;if(intrinsicBCoord.GetUnit()==eStyleUnit_Coord){result=intrinsicBCoord.GetCoordValue();}else{// We don't have an intrinsic bsize and we need aFrame's block-dir size.if(aFlags&BAIL_IF_REFLOW_NEEDED){returnNS_INTRINSIC_WIDTH_UNKNOWN;}// XXX Unfortunately, we probably don't know this yet, so this is wrong...// but it's not clear what we should do. If aFrame's inline size hasn't// been determined yet, we can't necessarily figure out its block size// either. For now, authors who put orthogonal elements into things like// buttons or table cells may have to explicitly provide sizes rather// than expecting intrinsic sizing to work "perfectly" in underspecified// cases.result=aFrame->BSize();}}else{result=aType==MIN_ISIZE?aFrame->GetMinISize(aRenderingContext):aFrame->GetPrefISize(aRenderingContext);}#ifdef DEBUG_INTRINSIC_WIDTH--gNoiseIndent;nsFrame::IndentBy(stderr,gNoiseIndent);static_cast<nsFrame*>(aFrame)->ListTag(stderr);printf_stderr(" %s %s intrinsic size from frame is %d.\n",aType==MIN_ISIZE?"min":"pref",horizontalAxis?"horizontal":"vertical",result);#endif// Handle elements with an intrinsic ratio (or size) and a specified// height, min-height, or max-height.// NOTE: We treat "min-height:auto" as "0" for the purpose of this code,// since that's what it means in all cases except for on flex items -- and// even there, we're supposed to ignore it (i.e. treat it as 0) until the// flex container explicitly considers it.constnsStyleCoord&styleBSize=horizontalAxis?stylePos->mHeight:stylePos->mWidth;constnsStyleCoord&styleMinBSize=horizontalAxis?stylePos->mMinHeight:stylePos->mMinWidth;constnsStyleCoord&styleMaxBSize=horizontalAxis?stylePos->mMaxHeight:stylePos->mMaxWidth;if(styleBSize.GetUnit()!=eStyleUnit_Auto||!(styleMinBSize.GetUnit()==eStyleUnit_Auto||(styleMinBSize.GetUnit()==eStyleUnit_Coord&&styleMinBSize.GetCoordValue()==0))||styleMaxBSize.GetUnit()!=eStyleUnit_None){nsSizeratio(aFrame->GetIntrinsicRatio());nscoordratioISize=(horizontalAxis?ratio.width:ratio.height);nscoordratioBSize=(horizontalAxis?ratio.height:ratio.width);if(ratioBSize!=0){AddStateBitToAncestors(aFrame,NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);nscoordbSizeTakenByBoxSizing=GetDefiniteSizeTakenByBoxSizing(boxSizing,aFrame,!isInlineAxis,aFlags&IGNORE_PADDING,aPercentageBasis);// NOTE: This is only the minContentSize if we've been passed MIN_INTRINSIC_ISIZE// (which is fine, because this should only be used inside a check for that flag).nscoordminContentSize=result;nscoordh;if(GetDefiniteSize(styleBSize,aFrame,!isInlineAxis,aPercentageBasis,&h)||(aPercentageBasis.isNothing()&&GetPercentBSize(styleBSize,aFrame,horizontalAxis,h))){h=std::max(0,h-bSizeTakenByBoxSizing);result=NSCoordMulDiv(h,ratioISize,ratioBSize);}if(GetDefiniteSize(styleMaxBSize,aFrame,!isInlineAxis,aPercentageBasis,&h)||(aPercentageBasis.isNothing()&&GetPercentBSize(styleMaxBSize,aFrame,horizontalAxis,h))){h=std::max(0,h-bSizeTakenByBoxSizing);nscoordmaxISize=NSCoordMulDiv(h,ratioISize,ratioBSize);if(maxISize<result){result=maxISize;}if(maxISize<minContentSize){minContentSize=maxISize;}}if(GetDefiniteSize(styleMinBSize,aFrame,!isInlineAxis,aPercentageBasis,&h)||(aPercentageBasis.isNothing()&&GetPercentBSize(styleMinBSize,aFrame,horizontalAxis,h))){h=std::max(0,h-bSizeTakenByBoxSizing);nscoordminISize=NSCoordMulDiv(h,ratioISize,ratioBSize);if(minISize>result){result=minISize;}if(minISize>minContentSize){minContentSize=minISize;}}if(MOZ_UNLIKELY(aFlags&nsLayoutUtils::MIN_INTRINSIC_ISIZE)){// This is the 'min-width/height:auto' "transferred size" piece of:// https://www.w3.org/TR/css-flexbox-1/#min-width-automatic-minimum-size// https://drafts.csswg.org/css-grid/#min-size-autoresult=std::min(result,minContentSize);}}}}if(aFrame->IsTableFrame()){// Tables can't shrink smaller than their intrinsic minimum width,// no matter what.min=aFrame->GetMinISize(aRenderingContext);}nsIFrame::IntrinsicISizeOffsetDataoffsets=MOZ_LIKELY(isInlineAxis)?aFrame->IntrinsicISizeOffsets():aFrame->IntrinsicBSizeOffsets();nscoordcontentBoxSize=result;result=AddIntrinsicSizeOffset(aRenderingContext,aFrame,offsets,aType,boxSizing,result,min,styleISize,haveFixedMinISize?&minISize:nullptr,styleMinISize,haveFixedMaxISize?&maxISize:nullptr,styleMaxISize,aFlags,aAxis);nscoordoverflow=result-aMarginBoxMinSizeClamp;if(MOZ_UNLIKELY(overflow>0)){nscoordnewContentBoxSize=std::max(nscoord(0),contentBoxSize-overflow);result-=contentBoxSize-newContentBoxSize;}#ifdef DEBUG_INTRINSIC_WIDTHnsFrame::IndentBy(stderr,gNoiseIndent);static_cast<nsFrame*>(aFrame)->ListTag(stderr);printf_stderr(" %s %s intrinsic size for container is %d twips.\n",aType==MIN_ISIZE?"min":"pref",horizontalAxis?"horizontal":"vertical",result);#endifreturnresult;}/* static */nscoordnsLayoutUtils::IntrinsicForContainer(gfxContext*aRenderingContext,nsIFrame*aFrame,IntrinsicISizeTypeaType,uint32_taFlags){MOZ_ASSERT(aFrame&&aFrame->GetParent());// We want the size aFrame will contribute to its parent's inline-size.PhysicalAxisaxis=aFrame->GetParent()->GetWritingMode().PhysicalAxis(eLogicalAxisInline);returnIntrinsicForAxis(axis,aRenderingContext,aFrame,aType,Nothing(),aFlags);}/* static */nscoordnsLayoutUtils::MinSizeContributionForAxis(PhysicalAxisaAxis,gfxContext*aRC,nsIFrame*aFrame,IntrinsicISizeTypeaType,uint32_taFlags){MOZ_ASSERT(aFrame);MOZ_ASSERT(aFrame->IsFlexOrGridItem(),"only grid/flex items have this behavior currently");#ifdef DEBUG_INTRINSIC_WIDTHnsFrame::IndentBy(stderr,gNoiseIndent);static_cast<nsFrame*>(aFrame)->ListTag(stderr);printf_stderr(" %s min-isize for %s WM:\n",aType==MIN_ISIZE?"min":"pref",aWM.IsVertical()?"vertical":"horizontal");#endif// Note: this method is only meant for grid/flex items which always// include percentages in their intrinsic size.aFlags|=nsLayoutUtils::ADD_PERCENTS;constnsStylePosition*conststylePos=aFrame->StylePosition();constnsStyleCoord*style=aAxis==eAxisHorizontal?&stylePos->mMinWidth:&stylePos->mMinHeight;nscoordminSize;nscoord*fixedMinSize=nullptr;autominSizeUnit=style->GetUnit();if(minSizeUnit==eStyleUnit_Auto){if(aFrame->StyleDisplay()->mOverflowX==NS_STYLE_OVERFLOW_VISIBLE){style=aAxis==eAxisHorizontal?&stylePos->mWidth:&stylePos->mHeight;if(GetAbsoluteCoord(*style,minSize)){// We have a definite width/height. This is the "specified size" in:// https://drafts.csswg.org/css-grid/#min-size-autofixedMinSize=&minSize;}// fall through - the caller will have to deal with "transferred size"}else{// min-[width|height]:auto with overflow != visible computes to zero.minSize=0;fixedMinSize=&minSize;}}elseif(GetAbsoluteCoord(*style,minSize)){fixedMinSize=&minSize;}elseif(minSizeUnit!=eStyleUnit_Enumerated){MOZ_ASSERT(style->HasPercent());minSize=0;fixedMinSize=&minSize;}if(!fixedMinSize){// Let the caller deal with the "content size" cases.#ifdef DEBUG_INTRINSIC_WIDTHnsFrame::IndentBy(stderr,gNoiseIndent);static_cast<nsFrame*>(aFrame)->ListTag(stderr);printf_stderr(" %s min-isize is indefinite.\n",aType==MIN_ISIZE?"min":"pref");#endifreturnNS_UNCONSTRAINEDSIZE;}// If aFrame is a container for font size inflation, then shrink// wrapping inside of it should not apply font size inflation.AutoMaybeDisableFontInflationan(aFrame);PhysicalAxisourInlineAxis=aFrame->GetWritingMode().PhysicalAxis(eLogicalAxisInline);nsIFrame::IntrinsicISizeOffsetDataoffsets=ourInlineAxis==aAxis?aFrame->IntrinsicISizeOffsets():aFrame->IntrinsicBSizeOffsets();nscoordresult=0;nscoordmin=0;constnsStyleCoord&maxISize=aAxis==eAxisHorizontal?stylePos->mMaxWidth:stylePos->mMaxHeight;result=AddIntrinsicSizeOffset(aRC,aFrame,offsets,aType,stylePos->mBoxSizing,result,min,*style,fixedMinSize,*style,nullptr,maxISize,aFlags,aAxis);#ifdef DEBUG_INTRINSIC_WIDTHnsFrame::IndentBy(stderr,gNoiseIndent);static_cast<nsFrame*>(aFrame)->ListTag(stderr);printf_stderr(" %s min-isize is %d twips.\n",aType==MIN_ISIZE?"min":"pref",result);#endifreturnresult;}/* static */nscoordnsLayoutUtils::ComputeCBDependentValue(nscoordaPercentBasis,constnsStyleCoord&aCoord){NS_WARNING_ASSERTION(aPercentBasis!=NS_UNCONSTRAINEDSIZE,"have unconstrained width or height; this should only result from very ""large sizes, not attempts at intrinsic size calculation");if(aCoord.IsCoordPercentCalcUnit()){returnnsRuleNode::ComputeCoordPercentCalc(aCoord,aPercentBasis);}NS_ASSERTION(aCoord.GetUnit()==eStyleUnit_None||aCoord.GetUnit()==eStyleUnit_Auto,"unexpected width value");return0;}/* static */nscoordnsLayoutUtils::ComputeBSizeDependentValue(nscoordaContainingBlockBSize,constnsStyleCoord&aCoord){// XXXldb Some callers explicitly check aContainingBlockBSize// against NS_AUTOHEIGHT *and* unit against eStyleUnit_Percent or// calc()s containing percents before calling this function.// However, it would be much more likely to catch problems without// the unit conditions.// XXXldb Many callers pass a non-'auto' containing block height when// according to CSS2.1 they should be passing 'auto'.NS_PRECONDITION(NS_AUTOHEIGHT!=aContainingBlockBSize||!aCoord.HasPercent(),"unexpected containing block block-size");if(aCoord.IsCoordPercentCalcUnit()){returnnsRuleNode::ComputeCoordPercentCalc(aCoord,aContainingBlockBSize);}NS_ASSERTION(aCoord.GetUnit()==eStyleUnit_None||aCoord.GetUnit()==eStyleUnit_Auto,"unexpected block-size value");return0;}/* static */voidnsLayoutUtils::MarkDescendantsDirty(nsIFrame*aSubtreeRoot){AutoTArray<nsIFrame*,4>subtrees;subtrees.AppendElement(aSubtreeRoot);// dirty descendants, iterating over subtrees that may include// additional subtrees associated with placeholdersdo{nsIFrame*subtreeRoot=subtrees.ElementAt(subtrees.Length()-1);subtrees.RemoveElementAt(subtrees.Length()-1);// Mark all descendants dirty (using an nsTArray stack rather than// recursion).// Note that ReflowInput::InitResizeFlags has some similar// code; see comments there for how and why it differs.AutoTArray<nsIFrame*,32>stack;stack.AppendElement(subtreeRoot);do{nsIFrame*f=stack.ElementAt(stack.Length()-1);stack.RemoveElementAt(stack.Length()-1);f->MarkIntrinsicISizesDirty();if(f->IsPlaceholderFrame()){nsIFrame*oof=nsPlaceholderFrame::GetRealFrameForPlaceholder(f);if(!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot,oof)){// We have another distinct subtree we need to mark.subtrees.AppendElement(oof);}}nsIFrame::ChildListIteratorlists(f);for(;!lists.IsDone();lists.Next()){nsFrameList::EnumeratorchildFrames(lists.CurrentList());for(;!childFrames.AtEnd();childFrames.Next()){nsIFrame*kid=childFrames.get();stack.AppendElement(kid);}}}while(stack.Length()!=0);}while(subtrees.Length()!=0);}/* static */voidnsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(nsIFrame*aFrame){AutoTArray<nsIFrame*,32>stack;stack.AppendElement(aFrame);do{nsIFrame*f=stack.ElementAt(stack.Length()-1);stack.RemoveElementAt(stack.Length()-1);if(!f->HasAnyStateBits(NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)){continue;}f->MarkIntrinsicISizesDirty();for(nsIFrame::ChildListIteratorlists(f);!lists.IsDone();lists.Next()){for(nsIFrame*kid:lists.CurrentList()){stack.AppendElement(kid);}}}while(stack.Length()!=0);}nsSizensLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(nscoordminWidth,nscoordminHeight,nscoordmaxWidth,nscoordmaxHeight,nscoordtentWidth,nscoordtentHeight){// Now apply min/max-width/height - CSS 2.1 sections 10.4 and 10.7:if(minWidth>maxWidth)maxWidth=minWidth;if(minHeight>maxHeight)maxHeight=minHeight;nscoordheightAtMaxWidth,heightAtMinWidth,widthAtMaxHeight,widthAtMinHeight;if(tentWidth>0){heightAtMaxWidth=NSCoordMulDiv(maxWidth,tentHeight,tentWidth);if(heightAtMaxWidth<minHeight)heightAtMaxWidth=minHeight;heightAtMinWidth=NSCoordMulDiv(minWidth,tentHeight,tentWidth);if(heightAtMinWidth>maxHeight)heightAtMinWidth=maxHeight;}else{heightAtMaxWidth=heightAtMinWidth=NS_CSS_MINMAX(tentHeight,minHeight,maxHeight);}if(tentHeight>0){widthAtMaxHeight=NSCoordMulDiv(maxHeight,tentWidth,tentHeight);if(widthAtMaxHeight<minWidth)widthAtMaxHeight=minWidth;widthAtMinHeight=NSCoordMulDiv(minHeight,tentWidth,tentHeight);if(widthAtMinHeight>maxWidth)widthAtMinHeight=maxWidth;}else{widthAtMaxHeight=widthAtMinHeight=NS_CSS_MINMAX(tentWidth,minWidth,maxWidth);}// The table at http://www.w3.org/TR/CSS21/visudet.html#min-max-widths :nscoordwidth,height;if(tentWidth>maxWidth){if(tentHeight>maxHeight){if(int64_t(maxWidth)*int64_t(tentHeight)<=int64_t(maxHeight)*int64_t(tentWidth)){width=maxWidth;height=heightAtMaxWidth;}else{width=widthAtMaxHeight;height=maxHeight;}}else{// This also covers "(w > max-width) and (h < min-height)" since in// that case (max-width/w < 1), and with (h < min-height):// max(max-width * h/w, min-height) == min-heightwidth=maxWidth;height=heightAtMaxWidth;}}elseif(tentWidth<minWidth){if(tentHeight<minHeight){if(int64_t(minWidth)*int64_t(tentHeight)<=int64_t(minHeight)*int64_t(tentWidth)){width=widthAtMinHeight;height=minHeight;}else{width=minWidth;height=heightAtMinWidth;}}else{// This also covers "(w < min-width) and (h > max-height)" since in// that case (min-width/w > 1), and with (h > max-height):// min(min-width * h/w, max-height) == max-heightwidth=minWidth;height=heightAtMinWidth;}}else{if(tentHeight>maxHeight){width=widthAtMaxHeight;height=maxHeight;}elseif(tentHeight<minHeight){width=widthAtMinHeight;height=minHeight;}else{width=tentWidth;height=tentHeight;}}returnnsSize(width,height);}/* static */nscoordnsLayoutUtils::MinISizeFromInline(nsIFrame*aFrame,gfxContext*aRenderingContext){NS_ASSERTION(!aFrame->IsContainerForFontSizeInflation(),"should not be container for font size inflation");nsIFrame::InlineMinISizeDatadata;DISPLAY_MIN_WIDTH(aFrame,data.mPrevLines);aFrame->AddInlineMinISize(aRenderingContext,&data);data.ForceBreak();returndata.mPrevLines;}/* static */nscoordnsLayoutUtils::PrefISizeFromInline(nsIFrame*aFrame,gfxContext*aRenderingContext){NS_ASSERTION(!aFrame->IsContainerForFontSizeInflation(),"should not be container for font size inflation");nsIFrame::InlinePrefISizeDatadata;DISPLAY_PREF_WIDTH(aFrame,data.mPrevLines);aFrame->AddInlinePrefISize(aRenderingContext,&data);data.ForceBreak();returndata.mPrevLines;}staticnscolorDarkenColor(nscoloraColor){uint16_thue,sat,value;uint8_talpha;// convert the RBG to HSV so we can get the lightness (which is the v)NS_RGB2HSV(aColor,hue,sat,value,alpha);// The goal here is to send white to black while letting colored// stuff stay colored... So we adopt the following approach.// Something with sat = 0 should end up with value = 0. Something// with a high sat can end up with a high value and it's ok.... At// the same time, we don't want to make things lighter. Do// something simple, since it seems to work.if(value>sat){value=sat;// convert this color back into the RGB color space.NS_HSV2RGB(aColor,hue,sat,value,alpha);}returnaColor;}// Check whether we should darken text/decoration colors. We need to do this if// background images and colors are being suppressed, because that means// light text will not be visible against the (presumed light-colored) background.staticboolShouldDarkenColors(nsPresContext*aPresContext){return!aPresContext->GetBackgroundColorDraw()&&!aPresContext->GetBackgroundImageDraw();}nscolornsLayoutUtils::DarkenColorIfNeeded(nsIFrame*aFrame,nscoloraColor){if(ShouldDarkenColors(aFrame->PresContext())){returnDarkenColor(aColor);}returnaColor;}gfxFloatnsLayoutUtils::GetSnappedBaselineY(nsIFrame*aFrame,gfxContext*aContext,nscoordaY,nscoordaAscent){gfxFloatappUnitsPerDevUnit=aFrame->PresContext()->AppUnitsPerDevPixel();gfxFloatbaseline=gfxFloat(aY)+aAscent;gfxRectputativeRect(0,baseline/appUnitsPerDevUnit,1,1);if(!aContext->UserToDevicePixelSnapped(putativeRect,true))returnbaseline;returnaContext->DeviceToUser(putativeRect.TopLeft()).y*appUnitsPerDevUnit;}gfxFloatnsLayoutUtils::GetSnappedBaselineX(nsIFrame*aFrame,gfxContext*aContext,nscoordaX,nscoordaAscent){gfxFloatappUnitsPerDevUnit=aFrame->PresContext()->AppUnitsPerDevPixel();gfxFloatbaseline=gfxFloat(aX)+aAscent;gfxRectputativeRect(baseline/appUnitsPerDevUnit,0,1,1);if(!aContext->UserToDevicePixelSnapped(putativeRect,true)){returnbaseline;}returnaContext->DeviceToUser(putativeRect.TopLeft()).x*appUnitsPerDevUnit;}// Hard limit substring lengths to 8000 characters ... this lets us statically// size the cluster buffer array in FindSafeLength#define MAX_GFX_TEXT_BUF_SIZE 8000staticint32_tFindSafeLength(constchar16_t*aString,uint32_taLength,uint32_taMaxChunkLength){if(aLength<=aMaxChunkLength)returnaLength;int32_tlen=aMaxChunkLength;// Ensure that we don't break inside a surrogate pairwhile(len>0&&NS_IS_LOW_SURROGATE(aString[len])){len--;}if(len==0){// We don't want our caller to go into an infinite loop, so don't// return zero. It's hard to imagine how we could actually get here// unless there are languages that allow clusters of arbitrary size.// If there are and someone feeds us a 500+ character cluster, too// bad.returnaMaxChunkLength;}returnlen;}staticint32_tGetMaxChunkLength(nsFontMetrics&aFontMetrics){returnstd::min(aFontMetrics.GetMaxStringLength(),MAX_GFX_TEXT_BUF_SIZE);}nscoordnsLayoutUtils::AppUnitWidthOfString(constchar16_t*aString,uint32_taLength,nsFontMetrics&aFontMetrics,DrawTarget*aDrawTarget){uint32_tmaxChunkLength=GetMaxChunkLength(aFontMetrics);nscoordwidth=0;while(aLength>0){int32_tlen=FindSafeLength(aString,aLength,maxChunkLength);width+=aFontMetrics.GetWidth(aString,len,aDrawTarget);aLength-=len;aString+=len;}returnwidth;}nscoordnsLayoutUtils::AppUnitWidthOfStringBidi(constchar16_t*aString,uint32_taLength,constnsIFrame*aFrame,nsFontMetrics&aFontMetrics,gfxContext&aContext){nsPresContext*presContext=aFrame->PresContext();if(presContext->BidiEnabled()){nsBidiLevellevel=nsBidiPresUtils::BidiLevelFromStyle(aFrame->StyleContext());returnnsBidiPresUtils::MeasureTextWidth(aString,aLength,level,presContext,aContext,aFontMetrics);}aFontMetrics.SetTextRunRTL(false);aFontMetrics.SetVertical(aFrame->GetWritingMode().IsVertical());aFontMetrics.SetTextOrientation(aFrame->StyleVisibility()->mTextOrientation);returnnsLayoutUtils::AppUnitWidthOfString(aString,aLength,aFontMetrics,aContext.GetDrawTarget());}boolnsLayoutUtils::StringWidthIsGreaterThan(constnsString&aString,nsFontMetrics&aFontMetrics,DrawTarget*aDrawTarget,nscoordaWidth){constchar16_t*string=aString.get();uint32_tlength=aString.Length();uint32_tmaxChunkLength=GetMaxChunkLength(aFontMetrics);nscoordwidth=0;while(length>0){int32_tlen=FindSafeLength(string,length,maxChunkLength);width+=aFontMetrics.GetWidth(string,len,aDrawTarget);if(width>aWidth){returntrue;}length-=len;string+=len;}returnfalse;}nsBoundingMetricsnsLayoutUtils::AppUnitBoundsOfString(constchar16_t*aString,uint32_taLength,nsFontMetrics&aFontMetrics,DrawTarget*aDrawTarget){uint32_tmaxChunkLength=GetMaxChunkLength(aFontMetrics);int32_tlen=FindSafeLength(aString,aLength,maxChunkLength);// Assign directly in the first iteration. This ensures that// negative ascent/descent can be returned and the left bearing// is properly initialized.nsBoundingMetricstotalMetrics=aFontMetrics.GetBoundingMetrics(aString,len,aDrawTarget);aLength-=len;aString+=len;while(aLength>0){len=FindSafeLength(aString,aLength,maxChunkLength);nsBoundingMetricsmetrics=aFontMetrics.GetBoundingMetrics(aString,len,aDrawTarget);totalMetrics+=metrics;aLength-=len;aString+=len;}returntotalMetrics;}voidnsLayoutUtils::DrawString(constnsIFrame*aFrame,nsFontMetrics&aFontMetrics,gfxContext*aContext,constchar16_t*aString,int32_taLength,nsPointaPoint,nsStyleContext*aStyleContext,DrawStringFlagsaFlags){nsresultrv=NS_ERROR_FAILURE;// If caller didn't pass a style context, use the frame's.if(!aStyleContext){aStyleContext=aFrame->StyleContext();}if(aFlags&DrawStringFlags::eForceHorizontal){aFontMetrics.SetVertical(false);}else{aFontMetrics.SetVertical(WritingMode(aStyleContext).IsVertical());}aFontMetrics.SetTextOrientation(aStyleContext->StyleVisibility()->mTextOrientation);nsPresContext*presContext=aFrame->PresContext();if(presContext->BidiEnabled()){nsBidiLevellevel=nsBidiPresUtils::BidiLevelFromStyle(aStyleContext);rv=nsBidiPresUtils::RenderText(aString,aLength,level,presContext,*aContext,aContext->GetDrawTarget(),aFontMetrics,aPoint.x,aPoint.y);}if(NS_FAILED(rv)){aFontMetrics.SetTextRunRTL(false);DrawUniDirString(aString,aLength,aPoint,aFontMetrics,*aContext);}}voidnsLayoutUtils::DrawUniDirString(constchar16_t*aString,uint32_taLength,nsPointaPoint,nsFontMetrics&aFontMetrics,gfxContext&aContext){nscoordx=aPoint.x;nscoordy=aPoint.y;uint32_tmaxChunkLength=GetMaxChunkLength(aFontMetrics);if(aLength<=maxChunkLength){aFontMetrics.DrawString(aString,aLength,x,y,&aContext,aContext.GetDrawTarget());return;}boolisRTL=aFontMetrics.GetTextRunRTL();// If we're drawing right to left, we must start at the end.if(isRTL){x+=nsLayoutUtils::AppUnitWidthOfString(aString,aLength,aFontMetrics,aContext.GetDrawTarget());}while(aLength>0){int32_tlen=FindSafeLength(aString,aLength,maxChunkLength);nscoordwidth=aFontMetrics.GetWidth(aString,len,aContext.GetDrawTarget());if(isRTL){x-=width;}aFontMetrics.DrawString(aString,len,x,y,&aContext,aContext.GetDrawTarget());if(!isRTL){x+=width;}aLength-=len;aString+=len;}}/* static */voidnsLayoutUtils::PaintTextShadow(constnsIFrame*aFrame,gfxContext*aContext,constnsRect&aTextRect,constnsRect&aDirtyRect,constnscolor&aForegroundColor,TextShadowCallbackaCallback,void*aCallbackData){constnsStyleText*textStyle=aFrame->StyleText();if(!textStyle->HasTextShadow())return;// Text shadow happens with the last value being painted at the back,// ie. it is painted first.gfxContext*aDestCtx=aContext;for(uint32_ti=textStyle->mTextShadow->Length();i>0;--i){nsCSSShadowItem*shadowDetails=textStyle->mTextShadow->ShadowAt(i-1);nsPointshadowOffset(shadowDetails->mXOffset,shadowDetails->mYOffset);nscoordblurRadius=std::max(shadowDetails->mRadius,0);nsRectshadowRect(aTextRect);shadowRect.MoveBy(shadowOffset);nsPresContext*presCtx=aFrame->PresContext();nsContextBoxBlurcontextBoxBlur;gfxContext*shadowContext=contextBoxBlur.Init(shadowRect,0,blurRadius,presCtx->AppUnitsPerDevPixel(),aDestCtx,aDirtyRect,nullptr);if(!shadowContext)continue;nscolorshadowColor;if(shadowDetails->mHasColor)shadowColor=shadowDetails->mColor;elseshadowColor=aForegroundColor;aDestCtx->Save();aDestCtx->NewPath();aDestCtx->SetColor(Color::FromABGR(shadowColor));// The callback will draw whatever we want to blur as a shadow.aCallback(shadowContext,shadowOffset,shadowColor,aCallbackData);contextBoxBlur.DoPaint();aDestCtx->Restore();}}/* static */nscoordnsLayoutUtils::GetCenteredFontBaseline(nsFontMetrics*aFontMetrics,nscoordaLineHeight,boolaIsInverted){nscoordfontAscent=aIsInverted?aFontMetrics->MaxDescent():aFontMetrics->MaxAscent();nscoordfontHeight=aFontMetrics->MaxHeight();nscoordleading=aLineHeight-fontHeight;returnfontAscent+leading/2;}/* static */boolnsLayoutUtils::GetFirstLineBaseline(WritingModeaWritingMode,constnsIFrame*aFrame,nscoord*aResult){LinePositionposition;if(!GetFirstLinePosition(aWritingMode,aFrame,&position))returnfalse;*aResult=position.mBaseline;returntrue;}/* static */boolnsLayoutUtils::GetFirstLinePosition(WritingModeaWM,constnsIFrame*aFrame,LinePosition*aResult){constnsBlockFrame*block=nsLayoutUtils::GetAsBlock(const_cast<nsIFrame*>(aFrame));if(!block){// For the first-line baseline we also have to check for a table, and if// so, use the baseline of its first row.LayoutFrameTypefType=aFrame->Type();if(fType==LayoutFrameType::TableWrapper||fType==LayoutFrameType::FlexContainer||fType==LayoutFrameType::GridContainer){if((fType==LayoutFrameType::GridContainer&&aFrame->HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE))||(fType==LayoutFrameType::FlexContainer&&aFrame->HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE))||(fType==LayoutFrameType::TableWrapper&&static_cast<constnsTableWrapperFrame*>(aFrame)->GetRowCount()==0)){// empty grid/flex/table containeraResult->mBStart=0;aResult->mBaseline=aFrame->SynthesizeBaselineBOffsetFromBorderBox(aWM,BaselineSharingGroup::eFirst);aResult->mBEnd=aFrame->BSize(aWM);returntrue;}aResult->mBStart=0;aResult->mBaseline=aFrame->GetLogicalBaseline(aWM);// This is what we want for the list bullet caller; not sure if// other future callers will want the same.aResult->mBEnd=aFrame->BSize(aWM);returntrue;}// For first-line baselines, we have to consider scroll frames.if(fType==LayoutFrameType::Scroll){nsIScrollableFrame*sFrame=do_QueryFrame(const_cast<nsIFrame*>(aFrame));if(!sFrame){NS_NOTREACHED("not scroll frame");}LinePositionkidPosition;if(GetFirstLinePosition(aWM,sFrame->GetScrolledFrame(),&kidPosition)){// Consider only the border and padding that contributes to the// kid's position, not the scrolling, so we get the initial// position.*aResult=kidPosition+aFrame->GetLogicalUsedBorderAndPadding(aWM).BStart(aWM);returntrue;}returnfalse;}if(fType==LayoutFrameType::FieldSet){LinePositionkidPosition;nsIFrame*kid=aFrame->PrincipalChildList().FirstChild();// kid might be a legend frame here, but that's ok.if(GetFirstLinePosition(aWM,kid,&kidPosition)){*aResult=kidPosition+kid->GetLogicalNormalPosition(aWM,aFrame->GetSize()).B(aWM);returntrue;}returnfalse;}// No baseline.returnfalse;}for(nsBlockFrame::ConstLineIteratorline=block->LinesBegin(),line_end=block->LinesEnd();line!=line_end;++line){if(line->IsBlock()){nsIFrame*kid=line->mFirstChild;LinePositionkidPosition;if(GetFirstLinePosition(aWM,kid,&kidPosition)){//XXX Not sure if this is the correct value to use for container// width here. It will only be used in vertical-rl layout,// which we don't have full support and testing for yet.constnsSize&containerSize=line->mContainerSize;*aResult=kidPosition+kid->GetLogicalNormalPosition(aWM,containerSize).B(aWM);returntrue;}}else{// XXX Is this the right test? We have some bogus empty lines// floating around, but IsEmpty is perhaps too weak.if(line->BSize()!=0||!line->IsEmpty()){nscoordbStart=line->BStart();aResult->mBStart=bStart;aResult->mBaseline=bStart+line->GetLogicalAscent();aResult->mBEnd=bStart+line->BSize();returntrue;}}}returnfalse;}/* static */boolnsLayoutUtils::GetLastLineBaseline(WritingModeaWM,constnsIFrame*aFrame,nscoord*aResult){constnsBlockFrame*block=nsLayoutUtils::GetAsBlock(const_cast<nsIFrame*>(aFrame));if(!block)// No baseline. (We intentionally don't descend into scroll frames.)returnfalse;for(nsBlockFrame::ConstReverseLineIteratorline=block->LinesRBegin(),line_end=block->LinesREnd();line!=line_end;++line){if(line->IsBlock()){nsIFrame*kid=line->mFirstChild;nscoordkidBaseline;constnsSize&containerSize=line->mContainerSize;if(GetLastLineBaseline(aWM,kid,&kidBaseline)){// Ignore relative positioning for baseline calculations*aResult=kidBaseline+kid->GetLogicalNormalPosition(aWM,containerSize).B(aWM);returntrue;}elseif(kid->IsScrollFrame()){// Defer to nsFrame::GetLogicalBaseline (which synthesizes a baseline// from the margin-box).kidBaseline=kid->GetLogicalBaseline(aWM);*aResult=kidBaseline+kid->GetLogicalNormalPosition(aWM,containerSize).B(aWM);returntrue;}}else{// XXX Is this the right test? We have some bogus empty lines// floating around, but IsEmpty is perhaps too weak.if(line->BSize()!=0||!line->IsEmpty()){*aResult=line->BStart()+line->GetLogicalAscent();returntrue;}}}returnfalse;}staticnscoordCalculateBlockContentBEnd(WritingModeaWM,nsBlockFrame*aFrame){NS_PRECONDITION(aFrame,"null ptr");nscoordcontentBEnd=0;for(nsBlockFrame::LineIteratorline=aFrame->LinesBegin(),line_end=aFrame->LinesEnd();line!=line_end;++line){if(line->IsBlock()){nsIFrame*child=line->mFirstChild;constnsSize&containerSize=line->mContainerSize;nscoordoffset=child->GetLogicalNormalPosition(aWM,containerSize).B(aWM);contentBEnd=std::max(contentBEnd,nsLayoutUtils::CalculateContentBEnd(aWM,child)+offset);}else{contentBEnd=std::max(contentBEnd,line->BEnd());}}returncontentBEnd;}/* static */nscoordnsLayoutUtils::CalculateContentBEnd(WritingModeaWM,nsIFrame*aFrame){NS_PRECONDITION(aFrame,"null ptr");nscoordcontentBEnd=aFrame->BSize(aWM);// We want scrollable overflow rather than visual because this// calculation is intended to affect layout.LogicalSizeoverflowSize(aWM,aFrame->GetScrollableOverflowRect().Size());if(overflowSize.BSize(aWM)>contentBEnd){nsIFrame::ChildListIDsskip(nsIFrame::kOverflowList|nsIFrame::kExcessOverflowContainersList|nsIFrame::kOverflowOutOfFlowList);nsBlockFrame*blockFrame=GetAsBlock(aFrame);if(blockFrame){contentBEnd=std::max(contentBEnd,CalculateBlockContentBEnd(aWM,blockFrame));skip|=nsIFrame::kPrincipalList;}nsIFrame::ChildListIteratorlists(aFrame);for(;!lists.IsDone();lists.Next()){if(!skip.Contains(lists.CurrentID())){nsFrameList::EnumeratorchildFrames(lists.CurrentList());for(;!childFrames.AtEnd();childFrames.Next()){nsIFrame*child=childFrames.get();nscoordoffset=child->GetLogicalNormalPosition(aWM,aFrame->GetSize()).B(aWM);contentBEnd=std::max(contentBEnd,CalculateContentBEnd(aWM,child)+offset);}}}}returncontentBEnd;}/* static */nsIFrame*nsLayoutUtils::GetClosestLayer(nsIFrame*aFrame){nsIFrame*layer;for(layer=aFrame;layer;layer=layer->GetParent()){if(layer->IsAbsPosContainingBlock()||(layer->GetParent()&&layer->GetParent()->IsScrollFrame()))break;}if(layer)returnlayer;returnaFrame->PresContext()->PresShell()->FrameManager()->GetRootFrame();}SamplingFilternsLayoutUtils::GetSamplingFilterForFrame(nsIFrame*aForFrame){SamplingFilterdefaultFilter=SamplingFilter::GOOD;nsStyleContext*sc;if(nsCSSRendering::IsCanvasFrame(aForFrame)){nsCSSRendering::FindBackground(aForFrame,&sc);}else{sc=aForFrame->StyleContext();}switch(sc->StyleVisibility()->mImageRendering){caseNS_STYLE_IMAGE_RENDERING_OPTIMIZESPEED:returnSamplingFilter::POINT;caseNS_STYLE_IMAGE_RENDERING_OPTIMIZEQUALITY:returnSamplingFilter::LINEAR;caseNS_STYLE_IMAGE_RENDERING_CRISPEDGES:returnSamplingFilter::POINT;default:returndefaultFilter;}}/** * Given an image being drawn into an appunit coordinate system, and * a point in that coordinate system, map the point back into image * pixel space. * @param aSize the size of the image, in pixels * @param aDest the rectangle that the image is being mapped into * @param aPt a point in the same coordinate system as the rectangle */staticgfxPointMapToFloatImagePixels(constgfxSize&aSize,constgfxRect&aDest,constgfxPoint&aPt){returngfxPoint(((aPt.x-aDest.X())*aSize.width)/aDest.Width(),((aPt.y-aDest.Y())*aSize.height)/aDest.Height());}/** * Given an image being drawn into an pixel-based coordinate system, and * a point in image space, map the point into the pixel-based coordinate * system. * @param aSize the size of the image, in pixels * @param aDest the rectangle that the image is being mapped into * @param aPt a point in image space */staticgfxPointMapToFloatUserPixels(constgfxSize&aSize,constgfxRect&aDest,constgfxPoint&aPt){returngfxPoint(aPt.x*aDest.Width()/aSize.width+aDest.X(),aPt.y*aDest.Height()/aSize.height+aDest.Y());}/* static */gfxRectnsLayoutUtils::RectToGfxRect(constnsRect&aRect,int32_taAppUnitsPerDevPixel){returngfxRect(gfxFloat(aRect.x)/aAppUnitsPerDevPixel,gfxFloat(aRect.y)/aAppUnitsPerDevPixel,gfxFloat(aRect.width)/aAppUnitsPerDevPixel,gfxFloat(aRect.height)/aAppUnitsPerDevPixel);}structSnappedImageDrawingParameters{// A transform from image space to device space.gfxMatriximageSpaceToDeviceSpace;// The size at which the image should be drawn (which may not be its// intrinsic size due to, for example, HQ scaling).nsIntSizesize;// The region in tiled image space which will be drawn, with an associated// region to which sampling should be restricted.ImageRegionregion;// The default viewport size for SVG images, which we use unless a different// one has been explicitly specified. This is the same as |size| except that// it does not take into account any transformation on the gfxContext we're// drawing to - for example, CSS transforms are not taken into account.CSSIntSizesvgViewportSize;// Whether there's anything to draw at all.boolshouldDraw;SnappedImageDrawingParameters():region(ImageRegion::Empty()),shouldDraw(false){}SnappedImageDrawingParameters(constgfxMatrix&aImageSpaceToDeviceSpace,constnsIntSize&aSize,constImageRegion&aRegion,constCSSIntSize&aSVGViewportSize):imageSpaceToDeviceSpace(aImageSpaceToDeviceSpace),size(aSize),region(aRegion),svgViewportSize(aSVGViewportSize),shouldDraw(true){}};/** * Given two axis-aligned rectangles, returns the transformation that maps the * first onto the second. * * @param aFrom The rect to be transformed. * @param aTo The rect that aFrom should be mapped onto by the transformation. */staticgfxMatrixTransformBetweenRects(constgfxRect&aFrom,constgfxRect&aTo){gfxSizescale(aTo.width/aFrom.width,aTo.height/aFrom.height);gfxPointtranslation(aTo.x-aFrom.x*scale.width,aTo.y-aFrom.y*scale.height);returngfxMatrix(scale.width,0,0,scale.height,translation.x,translation.y);}staticnsRectTileNearRect(constnsRect&aAnyTile,constnsRect&aTargetRect){nsPointdistance=aTargetRect.TopLeft()-aAnyTile.TopLeft();returnaAnyTile+nsPoint(distance.x/aAnyTile.width*aAnyTile.width,distance.y/aAnyTile.height*aAnyTile.height);}staticgfxFloatStableRound(gfxFloataValue){// Values slightly less than 0.5 should round up like 0.5 would; we're// assuming they were meant to be 0.5.returnfloor(aValue+0.5001);}staticgfxPointStableRound(constgfxPoint&aPoint){returngfxPoint(StableRound(aPoint.x),StableRound(aPoint.y));}/** * Given a set of input parameters, compute certain output parameters * for drawing an image with the image snapping algorithm. * See https://wiki.mozilla.org/Gecko:Image_Snapping_and_Rendering * * @see nsLayoutUtils::DrawImage() for the descriptions of input parameters */staticSnappedImageDrawingParametersComputeSnappedImageDrawingParameters(gfxContext*aCtx,int32_taAppUnitsPerDevPixel,constnsRectaDest,constnsRectaFill,constnsPointaAnchor,constnsRectaDirty,imgIContainer*aImage,constSamplingFilteraSamplingFilter,uint32_taImageFlags,ExtendModeaExtendMode){if(aDest.IsEmpty()||aFill.IsEmpty())returnSnappedImageDrawingParameters();// Avoid unnecessarily large offsets.booldoTile=!aDest.Contains(aFill);nsRectappUnitDest=doTile?TileNearRect(aDest,aFill.Intersect(aDirty)):aDest;nsPointanchor=aAnchor+(appUnitDest.TopLeft()-aDest.TopLeft());gfxRectdevPixelDest=nsLayoutUtils::RectToGfxRect(appUnitDest,aAppUnitsPerDevPixel);gfxRectdevPixelFill=nsLayoutUtils::RectToGfxRect(aFill,aAppUnitsPerDevPixel);gfxRectdevPixelDirty=nsLayoutUtils::RectToGfxRect(aDirty,aAppUnitsPerDevPixel);gfxMatrixcurrentMatrix=aCtx->CurrentMatrix();gfxRectfill=devPixelFill;gfxRectdest=devPixelDest;booldidSnap;// Snap even if we have a scale in the context. But don't snap if// we have something that's not translation+scale, or if the scale flips in// the X or Y direction, because snapped image drawing can't handle that yet.if(!currentMatrix.HasNonAxisAlignedTransform()&¤tMatrix._11>0.0&¤tMatrix._22>0.0&&aCtx->UserToDevicePixelSnapped(fill,true)&&aCtx->UserToDevicePixelSnapped(dest,true)){// We snapped. On this code path, |fill| and |dest| take into account// currentMatrix's transform.didSnap=true;}else{// We didn't snap. On this code path, |fill| and |dest| do not take into// account currentMatrix's transform.didSnap=false;fill=devPixelFill;dest=devPixelDest;}// If we snapped above, |dest| already takes into account |currentMatrix|'s scale// and has integer coordinates. If not, we need these properties to compute// the optimal drawn image size, so compute |snappedDestSize| here.gfxSizesnappedDestSize=dest.Size();if(!didSnap){gfxSizescaleFactors=currentMatrix.ScaleFactors(true);snappedDestSize.Scale(scaleFactors.width,scaleFactors.height);snappedDestSize.width=NS_round(snappedDestSize.width);snappedDestSize.height=NS_round(snappedDestSize.height);}// We need to be sure that this is at least one pixel in width and height,// or we'll end up drawing nothing even if we have a nonempty fill.snappedDestSize.width=std::max(snappedDestSize.width,1.0);snappedDestSize.height=std::max(snappedDestSize.height,1.0);// Bail if we're not going to end up drawing anything.if(fill.IsEmpty()||snappedDestSize.IsEmpty()){returnSnappedImageDrawingParameters();}nsIntSizeintImageSize=aImage->OptimalImageSizeForDest(snappedDestSize,imgIContainer::FRAME_CURRENT,aSamplingFilter,aImageFlags);gfxSizeimageSize(intImageSize.width,intImageSize.height);// XXX(seth): May be buggy; see bug 1151016.CSSIntSizesvgViewportSize=currentMatrix.IsIdentity()?CSSIntSize(intImageSize.width,intImageSize.height):CSSIntSize::Truncate(devPixelDest.width,devPixelDest.height);// Compute the set of pixels that would be sampled by an ideal renderinggfxPointsubimageTopLeft=MapToFloatImagePixels(imageSize,devPixelDest,devPixelFill.TopLeft());gfxPointsubimageBottomRight=MapToFloatImagePixels(imageSize,devPixelDest,devPixelFill.BottomRight());gfxRectsubimage;subimage.MoveTo(NSToIntFloor(subimageTopLeft.x),NSToIntFloor(subimageTopLeft.y));subimage.SizeTo(NSToIntCeil(subimageBottomRight.x)-subimage.x,NSToIntCeil(subimageBottomRight.y)-subimage.y);if(subimage.IsEmpty()){// Bail if the subimage is empty (we're not going to be drawing anything).returnSnappedImageDrawingParameters();}gfxMatrixtransform;gfxMatrixinvTransform;boolanchorAtUpperLeft=anchor.x==appUnitDest.x&&anchor.y==appUnitDest.y;boolexactlyOneImageCopy=aFill.IsEqualEdges(appUnitDest);if(anchorAtUpperLeft&&exactlyOneImageCopy){// The simple case: we can ignore the anchor point and compute the// transformation from the sampled region (the subimage) to the fill rect.// This approach is preferable when it works since it tends to produce// less numerical error.transform=TransformBetweenRects(subimage,fill);invTransform=TransformBetweenRects(fill,subimage);}else{// The more complicated case: we compute the transformation from the// image rect positioned at the image space anchor point to the dest rect// positioned at the device space anchor point.// Compute the anchor point in both device space and image space. This// code assumes that pixel-based devices have one pixel per device unit!gfxPointanchorPoint(gfxFloat(anchor.x)/aAppUnitsPerDevPixel,gfxFloat(anchor.y)/aAppUnitsPerDevPixel);gfxPointimageSpaceAnchorPoint=MapToFloatImagePixels(imageSize,devPixelDest,anchorPoint);if(didSnap){imageSpaceAnchorPoint=StableRound(imageSpaceAnchorPoint);anchorPoint=imageSpaceAnchorPoint;anchorPoint=MapToFloatUserPixels(imageSize,devPixelDest,anchorPoint);anchorPoint=currentMatrix.Transform(anchorPoint);anchorPoint=StableRound(anchorPoint);}// Compute an unsnapped version of the dest rect's size. We continue to// follow the pattern that we take |currentMatrix| into account only if// |didSnap| is true.gfxSizeunsnappedDestSize=didSnap?devPixelDest.Size()*currentMatrix.ScaleFactors(true):devPixelDest.Size();gfxRectanchoredDestRect(anchorPoint,unsnappedDestSize);gfxRectanchoredImageRect(imageSpaceAnchorPoint,imageSize);// Calculate anchoredDestRect with snapped fill rect when the devPixelFill rect// corresponds to just a single tile in that directionif(fill.Width()!=devPixelFill.Width()&&devPixelDest.x==devPixelFill.x&&devPixelDest.XMost()==devPixelFill.XMost()){anchoredDestRect.width=fill.width;}if(fill.Height()!=devPixelFill.Height()&&devPixelDest.y==devPixelFill.y&&devPixelDest.YMost()==devPixelFill.YMost()){anchoredDestRect.height=fill.height;}transform=TransformBetweenRects(anchoredImageRect,anchoredDestRect);invTransform=TransformBetweenRects(anchoredDestRect,anchoredImageRect);}// If the transform is not a straight translation by integers, then// filtering will occur, and restricting the fill rect to the dirty rect// would change the values computed for edge pixels, which we can't allow.// Also, if 'didSnap' is false then rounding out 'devPixelDirty' might not// produce pixel-aligned coordinates, which would also break the values// computed for edge pixels.if(didSnap&&!invTransform.HasNonIntegerTranslation()){// This form of Transform is safe to call since non-axis-aligned// transforms wouldn't be snapped.devPixelDirty=currentMatrix.Transform(devPixelDirty);devPixelDirty.RoundOut();fill=fill.Intersect(devPixelDirty);}if(fill.IsEmpty())returnSnappedImageDrawingParameters();gfxRectimageSpaceFill(didSnap?invTransform.Transform(fill):invTransform.TransformBounds(fill));// If we didn't snap, we need to post-multiply the matrix on the context to// get the final matrix we'll draw with, because we didn't take it into// account when computing the matrices above.if(!didSnap){transform=transform*currentMatrix;}ExtendModeextendMode=(aImageFlags&imgIContainer::FLAG_CLAMP)?ExtendMode::CLAMP:aExtendMode;// We were passed in the default extend mode but need to tile.if(extendMode==ExtendMode::CLAMP&&doTile){MOZ_ASSERT(!(aImageFlags&imgIContainer::FLAG_CLAMP));extendMode=ExtendMode::REPEAT;}ImageRegionregion=ImageRegion::CreateWithSamplingRestriction(imageSpaceFill,subimage,extendMode);returnSnappedImageDrawingParameters(transform,intImageSize,region,svgViewportSize);}staticDrawResultDrawImageInternal(gfxContext&aContext,nsPresContext*aPresContext,imgIContainer*aImage,constSamplingFilteraSamplingFilter,constnsRect&aDest,constnsRect&aFill,constnsPoint&aAnchor,constnsRect&aDirty,constMaybe<SVGImageContext>&aSVGContext,uint32_taImageFlags,ExtendModeaExtendMode=ExtendMode::CLAMP,floataOpacity=1.0){DrawResultresult=DrawResult::SUCCESS;aImageFlags|=imgIContainer::FLAG_ASYNC_NOTIFY;if(aPresContext->Type()==nsPresContext::eContext_Print){// We want vector images to be passed on as vector commands, not a raster// image.aImageFlags|=imgIContainer::FLAG_BYPASS_SURFACE_CACHE;}if(aDest.Contains(aFill)){aImageFlags|=imgIContainer::FLAG_CLAMP;}int32_tappUnitsPerDevPixel=aPresContext->AppUnitsPerDevPixel();SnappedImageDrawingParametersparams=ComputeSnappedImageDrawingParameters(&aContext,appUnitsPerDevPixel,aDest,aFill,aAnchor,aDirty,aImage,aSamplingFilter,aImageFlags,aExtendMode);if(!params.shouldDraw){returnresult;}{gfxContextMatrixAutoSaveRestorecontextMatrixRestorer(&aContext);RefPtr<gfxContext>destCtx=&aContext;destCtx->SetMatrix(params.imageSpaceToDeviceSpace);Maybe<SVGImageContext>fallbackContext;if(!aSVGContext){// Use the default viewport.fallbackContext.emplace(Some(params.svgViewportSize));}result=aImage->Draw(destCtx,params.size,params.region,imgIContainer::FRAME_CURRENT,aSamplingFilter,aSVGContext?aSVGContext:fallbackContext,aImageFlags,aOpacity);}returnresult;}/* static */DrawResultnsLayoutUtils::DrawSingleUnscaledImage(gfxContext&aContext,nsPresContext*aPresContext,imgIContainer*aImage,constSamplingFilteraSamplingFilter,constnsPoint&aDest,constnsRect*aDirty,uint32_taImageFlags,constnsRect*aSourceArea){CSSIntSizeimageSize;aImage->GetWidth(&imageSize.width);aImage->GetHeight(&imageSize.height);if(imageSize.width<1||imageSize.height<1){NS_WARNING("Image width or height is non-positive");returnDrawResult::TEMPORARY_ERROR;}nsSizesize(CSSPixel::ToAppUnits(imageSize));nsRectsource;if(aSourceArea){source=*aSourceArea;}else{source.SizeTo(size);}nsRectdest(aDest-source.TopLeft(),size);nsRectfill(aDest,source.Size());// Ensure that only a single image tile is drawn. If aSourceArea extends// outside the image bounds, we want to honor the aSourceArea-to-aDest// translation but we don't want to actually tile the image.fill.IntersectRect(fill,dest);returnDrawImageInternal(aContext,aPresContext,aImage,aSamplingFilter,dest,fill,aDest,aDirty?*aDirty:dest,/* no SVGImageContext */Nothing(),aImageFlags);}/* static */DrawResultnsLayoutUtils::DrawSingleImage(gfxContext&aContext,nsPresContext*aPresContext,imgIContainer*aImage,constSamplingFilteraSamplingFilter,constnsRect&aDest,constnsRect&aDirty,constMaybe<SVGImageContext>&aSVGContext,uint32_taImageFlags,constnsPoint*aAnchorPoint,constnsRect*aSourceArea){nscoordappUnitsPerCSSPixel=nsDeviceContext::AppUnitsPerCSSPixel();CSSIntSizepixelImageSize(ComputeSizeForDrawingWithFallback(aImage,aDest.Size()));if(pixelImageSize.width<1||pixelImageSize.height<1){NS_ASSERTION(pixelImageSize.width>=0&&pixelImageSize.height>=0,"Image width or height is negative");returnDrawResult::SUCCESS;// no point in drawing a zero size image}nsSizeimageSize(CSSPixel::ToAppUnits(pixelImageSize));nsRectsource;nsCOMPtr<imgIContainer>image;if(aSourceArea){source=*aSourceArea;nsIntRectsubRect(source.x,source.y,source.width,source.height);subRect.ScaleInverseRoundOut(appUnitsPerCSSPixel);image=ImageOps::Clip(aImage,subRect);nsRectimageRect;imageRect.SizeTo(imageSize);nsRectclippedSource=imageRect.Intersect(source);source-=clippedSource.TopLeft();imageSize=clippedSource.Size();}else{source.SizeTo(imageSize);image=aImage;}nsRectdest=GetWholeImageDestination(imageSize,source,aDest);// Ensure that only a single image tile is drawn. If aSourceArea extends// outside the image bounds, we want to honor the aSourceArea-to-aDest// transform but we don't want to actually tile the image.nsRectfill;fill.IntersectRect(aDest,dest);returnDrawImageInternal(aContext,aPresContext,image,aSamplingFilter,dest,fill,aAnchorPoint?*aAnchorPoint:fill.TopLeft(),aDirty,aSVGContext,aImageFlags);}/* static */voidnsLayoutUtils::ComputeSizeForDrawing(imgIContainer*aImage,CSSIntSize&aImageSize,/*outparam*/nsSize&aIntrinsicRatio,/*outparam*/bool&aGotWidth,/*outparam*/bool&aGotHeight/*outparam*/){aGotWidth=NS_SUCCEEDED(aImage->GetWidth(&aImageSize.width));aGotHeight=NS_SUCCEEDED(aImage->GetHeight(&aImageSize.height));boolgotRatio=NS_SUCCEEDED(aImage->GetIntrinsicRatio(&aIntrinsicRatio));if(!(aGotWidth&&aGotHeight)&&!gotRatio){// We hit an error (say, because the image failed to load or couldn't be// decoded) and should return zero size.aGotWidth=aGotHeight=true;aImageSize=CSSIntSize(0,0);aIntrinsicRatio=nsSize(0,0);}}/* static */CSSIntSizensLayoutUtils::ComputeSizeForDrawingWithFallback(imgIContainer*aImage,constnsSize&aFallbackSize){CSSIntSizeimageSize;nsSizeimageRatio;boolgotHeight,gotWidth;ComputeSizeForDrawing(aImage,imageSize,imageRatio,gotWidth,gotHeight);// If we didn't get both width and height, try to compute them using the// intrinsic ratio of the image.if(gotWidth!=gotHeight){if(!gotWidth){if(imageRatio.height!=0){imageSize.width=NSCoordSaturatingNonnegativeMultiply(imageSize.height,float(imageRatio.width)/float(imageRatio.height));gotWidth=true;}}else{if(imageRatio.width!=0){imageSize.height=NSCoordSaturatingNonnegativeMultiply(imageSize.width,float(imageRatio.height)/float(imageRatio.width));gotHeight=true;}}}// If we still don't have a width or height, just use the fallback size the// caller provided.if(!gotWidth){imageSize.width=nsPresContext::AppUnitsToIntCSSPixels(aFallbackSize.width);}if(!gotHeight){imageSize.height=nsPresContext::AppUnitsToIntCSSPixels(aFallbackSize.height);}returnimageSize;}/* static */nsPointnsLayoutUtils::GetBackgroundFirstTilePos(constnsPoint&aDest,constnsPoint&aFill,constnsSize&aRepeatSize){returnnsPoint(NSToIntFloor(float(aFill.x-aDest.x)/aRepeatSize.width)*aRepeatSize.width,NSToIntFloor(float(aFill.y-aDest.y)/aRepeatSize.height)*aRepeatSize.height)+aDest;}/* static */DrawResultnsLayoutUtils::DrawBackgroundImage(gfxContext&aContext,nsIFrame*aForFrame,nsPresContext*aPresContext,imgIContainer*aImage,constCSSIntSize&aImageSize,SamplingFilteraSamplingFilter,constnsRect&aDest,constnsRect&aFill,constnsSize&aRepeatSize,constnsPoint&aAnchor,constnsRect&aDirty,uint32_taImageFlags,ExtendModeaExtendMode,floataOpacity){AUTO_PROFILER_LABEL("nsLayoutUtils::DrawBackgroundImage",GRAPHICS);Maybe<SVGImageContext>svgContext(Some(SVGImageContext(Some(aImageSize))));SVGImageContext::MaybeStoreContextPaint(svgContext,aForFrame,aImage);/* Fast path when there is no need for image spacing */if(aRepeatSize.width==aDest.width&&aRepeatSize.height==aDest.height){returnDrawImageInternal(aContext,aPresContext,aImage,aSamplingFilter,aDest,aFill,aAnchor,aDirty,svgContext,aImageFlags,aExtendMode,aOpacity);}nsPointfirstTilePos=GetBackgroundFirstTilePos(aDest.TopLeft(),aFill.TopLeft(),aRepeatSize);for(int32_ti=firstTilePos.x;i<aFill.XMost();i+=aRepeatSize.width){for(int32_tj=firstTilePos.y;j<aFill.YMost();j+=aRepeatSize.height){nsRectdest(i,j,aDest.width,aDest.height);DrawResultresult=DrawImageInternal(aContext,aPresContext,aImage,aSamplingFilter,dest,dest,aAnchor,aDirty,svgContext,aImageFlags,ExtendMode::CLAMP,aOpacity);if(result!=DrawResult::SUCCESS){returnresult;}}}returnDrawResult::SUCCESS;}/* static */DrawResultnsLayoutUtils::DrawImage(gfxContext&aContext,nsStyleContext*aStyleContext,nsPresContext*aPresContext,imgIContainer*aImage,constSamplingFilteraSamplingFilter,constnsRect&aDest,constnsRect&aFill,constnsPoint&aAnchor,constnsRect&aDirty,uint32_taImageFlags,floataOpacity){Maybe<SVGImageContext>svgContext;SVGImageContext::MaybeStoreContextPaint(svgContext,aStyleContext,aImage);returnDrawImageInternal(aContext,aPresContext,aImage,aSamplingFilter,aDest,aFill,aAnchor,aDirty,svgContext,aImageFlags,ExtendMode::CLAMP,aOpacity);}/* static */nsRectnsLayoutUtils::GetWholeImageDestination(constnsSize&aWholeImageSize,constnsRect&aImageSourceArea,constnsRect&aDestArea){doublescaleX=double(aDestArea.width)/aImageSourceArea.width;doublescaleY=double(aDestArea.height)/aImageSourceArea.height;nscoorddestOffsetX=NSToCoordRound(aImageSourceArea.x*scaleX);nscoorddestOffsetY=NSToCoordRound(aImageSourceArea.y*scaleY);nscoordwholeSizeX=NSToCoordRound(aWholeImageSize.width*scaleX);nscoordwholeSizeY=NSToCoordRound(aWholeImageSize.height*scaleY);returnnsRect(aDestArea.TopLeft()-nsPoint(destOffsetX,destOffsetY),nsSize(wholeSizeX,wholeSizeY));}/* static */already_AddRefed<imgIContainer>nsLayoutUtils::OrientImage(imgIContainer*aContainer,constnsStyleImageOrientation&aOrientation){MOZ_ASSERT(aContainer,"Should have an image container");nsCOMPtr<imgIContainer>img(aContainer);if(aOrientation.IsFromImage()){img=ImageOps::Orient(img,img->GetOrientation());}elseif(!aOrientation.IsDefault()){Angleangle=aOrientation.Angle();Flipflip=aOrientation.IsFlipped()?Flip::Horizontal:Flip::Unflipped;img=ImageOps::Orient(img,Orientation(angle,flip));}returnimg.forget();}staticboolNonZeroStyleCoord(constnsStyleCoord&aCoord){if(aCoord.IsCoordPercentCalcUnit()){// Since negative results are clamped to 0, check > 0.returnnsRuleNode::ComputeCoordPercentCalc(aCoord,nscoord_MAX)>0||nsRuleNode::ComputeCoordPercentCalc(aCoord,0)>0;}returntrue;}/* static */boolnsLayoutUtils::HasNonZeroCorner(constnsStyleCorners&aCorners){NS_FOR_CSS_HALF_CORNERS(corner){if(NonZeroStyleCoord(aCorners.Get(corner)))returntrue;}returnfalse;}// aCorner is a "full corner" value, i.e. eCornerTopLeft etc.staticboolIsCornerAdjacentToSide(uint8_taCorner,SideaSide){static_assert((int)eSideTop==eCornerTopLeft,"Check for Full Corner");static_assert((int)eSideRight==eCornerTopRight,"Check for Full Corner");static_assert((int)eSideBottom==eCornerBottomRight,"Check for Full Corner");static_assert((int)eSideLeft==eCornerBottomLeft,"Check for Full Corner");static_assert((int)eSideTop==((eCornerTopRight-1)&3),"Check for Full Corner");static_assert((int)eSideRight==((eCornerBottomRight-1)&3),"Check for Full Corner");static_assert((int)eSideBottom==((eCornerBottomLeft-1)&3),"Check for Full Corner");static_assert((int)eSideLeft==((eCornerTopLeft-1)&3),"Check for Full Corner");returnaSide==aCorner||aSide==((aCorner-1)&3);}/* static */boolnsLayoutUtils::HasNonZeroCornerOnSide(constnsStyleCorners&aCorners,SideaSide){static_assert(eCornerTopLeftX/2==eCornerTopLeft,"Check for Non Zero on side");static_assert(eCornerTopLeftY/2==eCornerTopLeft,"Check for Non Zero on side");static_assert(eCornerTopRightX/2==eCornerTopRight,"Check for Non Zero on side");static_assert(eCornerTopRightY/2==eCornerTopRight,"Check for Non Zero on side");static_assert(eCornerBottomRightX/2==eCornerBottomRight,"Check for Non Zero on side");static_assert(eCornerBottomRightY/2==eCornerBottomRight,"Check for Non Zero on side");static_assert(eCornerBottomLeftX/2==eCornerBottomLeft,"Check for Non Zero on side");static_assert(eCornerBottomLeftY/2==eCornerBottomLeft,"Check for Non Zero on side");NS_FOR_CSS_HALF_CORNERS(corner){// corner is a "half corner" value, so dividing by two gives us a// "full corner" value.if(NonZeroStyleCoord(aCorners.Get(corner))&&IsCornerAdjacentToSide(corner/2,aSide))returntrue;}returnfalse;}/* static */nsTransparencyModensLayoutUtils::GetFrameTransparency(nsIFrame*aBackgroundFrame,nsIFrame*aCSSRootFrame){if(aCSSRootFrame->StyleEffects()->mOpacity<1.0f)returneTransparencyTransparent;if(HasNonZeroCorner(aCSSRootFrame->StyleBorder()->mBorderRadius))returneTransparencyTransparent;if(aCSSRootFrame->StyleDisplay()->mAppearance==NS_THEME_WIN_GLASS)returneTransparencyGlass;if(aCSSRootFrame->StyleDisplay()->mAppearance==NS_THEME_WIN_BORDERLESS_GLASS)returneTransparencyBorderlessGlass;nsITheme::Transparencytransparency;if(aCSSRootFrame->IsThemed(&transparency))returntransparency==nsITheme::eTransparent?eTransparencyTransparent:eTransparencyOpaque;// We need an uninitialized window to be treated as opaque because// doing otherwise breaks window display effects on some platforms,// specifically Vista. (bug 450322)if(aBackgroundFrame->IsViewportFrame()&&!aBackgroundFrame->PrincipalChildList().FirstChild()){returneTransparencyOpaque;}nsStyleContext*bgSC;if(!nsCSSRendering::FindBackground(aBackgroundFrame,&bgSC)){returneTransparencyTransparent;}constnsStyleBackground*bg=bgSC->StyleBackground();if(NS_GET_A(bg->BackgroundColor(bgSC))<255||// bottom layer's clip is used for the colorbg->BottomLayer().mClip!=StyleGeometryBox::BorderBox)returneTransparencyTransparent;returneTransparencyOpaque;}staticboolIsPopupFrame(nsIFrame*aFrame){// aFrame is a popup it's the list control frame dropdown for a combobox.LayoutFrameTypeframeType=aFrame->Type();if(frameType==LayoutFrameType::ListControl){nsListControlFrame*lcf=static_cast<nsListControlFrame*>(aFrame);returnlcf->IsInDropDownMode();}// ... or if it's a XUL menupopup frame.returnframeType==LayoutFrameType::MenuPopup;}/* static */boolnsLayoutUtils::IsPopup(nsIFrame*aFrame){// Optimization: the frame can't possibly be a popup if it has no view.if(!aFrame->HasView()){NS_ASSERTION(!IsPopupFrame(aFrame),"popup frame must have a view");returnfalse;}returnIsPopupFrame(aFrame);}/* static */nsIFrame*nsLayoutUtils::GetDisplayRootFrame(nsIFrame*aFrame){// We could use GetRootPresContext() here if the// NS_FRAME_IN_POPUP frame bit is set.nsIFrame*f=aFrame;for(;;){if(!f->HasAnyStateBits(NS_FRAME_IN_POPUP)){f=f->PresContext()->FrameManager()->GetRootFrame();}elseif(IsPopup(f)){returnf;}nsIFrame*parent=GetCrossDocParentFrame(f);if(!parent)returnf;f=parent;}}/* static */nsIFrame*nsLayoutUtils::GetReferenceFrame(nsIFrame*aFrame){nsIFrame*f=aFrame;for(;;){constnsStyleDisplay*disp=f->StyleDisplay();if(f->IsTransformed(disp)||f->IsPreserve3DLeaf(disp)||IsPopup(f)){returnf;}nsIFrame*parent=GetCrossDocParentFrame(f);if(!parent){returnf;}f=parent;}}/* static */gfx::ShapedTextFlagsnsLayoutUtils::GetTextRunFlagsForStyle(nsStyleContext*aStyleContext,constnsStyleFont*aStyleFont,constnsStyleText*aStyleText,nscoordaLetterSpacing){gfx::ShapedTextFlagsresult=gfx::ShapedTextFlags();if(aLetterSpacing!=0||aStyleText->mTextJustify==StyleTextJustify::InterCharacter){result|=gfx::ShapedTextFlags::TEXT_DISABLE_OPTIONAL_LIGATURES;}if(aStyleText->mControlCharacterVisibility==NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN){result|=gfx::ShapedTextFlags::TEXT_HIDE_CONTROL_CHARACTERS;}switch(aStyleContext->StyleText()->mTextRendering){caseNS_STYLE_TEXT_RENDERING_OPTIMIZESPEED:result|=gfx::ShapedTextFlags::TEXT_OPTIMIZE_SPEED;break;caseNS_STYLE_TEXT_RENDERING_AUTO:if(aStyleFont->mFont.size<aStyleContext->PresContext()->GetAutoQualityMinFontSize()){result|=gfx::ShapedTextFlags::TEXT_OPTIMIZE_SPEED;}break;default:break;}returnresult|GetTextRunOrientFlagsForStyle(aStyleContext);}/* static */gfx::ShapedTextFlagsnsLayoutUtils::GetTextRunOrientFlagsForStyle(nsStyleContext*aStyleContext){uint8_twritingMode=aStyleContext->StyleVisibility()->mWritingMode;switch(writingMode){caseNS_STYLE_WRITING_MODE_HORIZONTAL_TB:returngfx::ShapedTextFlags::TEXT_ORIENT_HORIZONTAL;caseNS_STYLE_WRITING_MODE_VERTICAL_LR:caseNS_STYLE_WRITING_MODE_VERTICAL_RL:switch(aStyleContext->StyleVisibility()->mTextOrientation){caseNS_STYLE_TEXT_ORIENTATION_MIXED:returngfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED;caseNS_STYLE_TEXT_ORIENTATION_UPRIGHT:returngfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;caseNS_STYLE_TEXT_ORIENTATION_SIDEWAYS:returngfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;default:NS_NOTREACHED("unknown text-orientation");returngfx::ShapedTextFlags();}caseNS_STYLE_WRITING_MODE_SIDEWAYS_LR:returngfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT;caseNS_STYLE_WRITING_MODE_SIDEWAYS_RL:returngfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;default:NS_NOTREACHED("unknown writing-mode");returngfx::ShapedTextFlags();}}/* static */voidnsLayoutUtils::GetRectDifferenceStrips(constnsRect&aR1,constnsRect&aR2,nsRect*aHStrip,nsRect*aVStrip){NS_ASSERTION(aR1.TopLeft()==aR2.TopLeft(),"expected rects at the same position");nsRectunionRect(aR1.x,aR1.y,std::max(aR1.width,aR2.width),std::max(aR1.height,aR2.height));nscoordVStripStart=std::min(aR1.width,aR2.width);nscoordHStripStart=std::min(aR1.height,aR2.height);*aVStrip=unionRect;aVStrip->x+=VStripStart;aVStrip->width-=VStripStart;*aHStrip=unionRect;aHStrip->y+=HStripStart;aHStrip->height-=HStripStart;}nsDeviceContext*nsLayoutUtils::GetDeviceContextForScreenInfo(nsPIDOMWindowOuter*aWindow){if(!aWindow){returnnullptr;}nsCOMPtr<nsIDocShell>docShell=aWindow->GetDocShell();while(docShell){// Now make sure our size is up to date. That will mean that the device// context does the right thing on multi-monitor systems when we return it to// the caller. It will also make sure that our prescontext has been created,// if we're supposed to have one.nsCOMPtr<nsPIDOMWindowOuter>win=docShell->GetWindow();if(!win){// No reason to go onreturnnullptr;}win->EnsureSizeAndPositionUpToDate();RefPtr<nsPresContext>presContext;docShell->GetPresContext(getter_AddRefs(presContext));if(presContext){nsDeviceContext*context=presContext->DeviceContext();if(context){returncontext;}}nsCOMPtr<nsIDocShellTreeItem>parentItem;docShell->GetParent(getter_AddRefs(parentItem));docShell=do_QueryInterface(parentItem);}returnnullptr;}/* static */boolnsLayoutUtils::IsReallyFixedPos(nsIFrame*aFrame){NS_PRECONDITION(aFrame->GetParent(),"IsReallyFixedPos called on frame not in tree");NS_PRECONDITION(aFrame->StyleDisplay()->mPosition==NS_STYLE_POSITION_FIXED,"IsReallyFixedPos called on non-'position:fixed' frame");LayoutFrameTypeparentType=aFrame->GetParent()->Type();returnparentType==LayoutFrameType::Viewport||parentType==LayoutFrameType::PageContent;}nsLayoutUtils::SurfaceFromElementResultnsLayoutUtils::SurfaceFromOffscreenCanvas(OffscreenCanvas*aOffscreenCanvas,uint32_taSurfaceFlags,RefPtr<DrawTarget>&aTarget){SurfaceFromElementResultresult;nsIntSizesize=aOffscreenCanvas->GetWidthHeight();result.mSourceSurface=aOffscreenCanvas->GetSurfaceSnapshot(&result.mAlphaType);if(!result.mSourceSurface){// If the element doesn't have a context then we won't get a snapshot. The canvas spec wants us to not error and just// draw nothing, so return an empty surface.result.mAlphaType=gfxAlphaType::Opaque;DrawTarget*ref=aTarget?aTarget.get():gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();RefPtr<DrawTarget>dt=ref->CreateSimilarDrawTarget(IntSize(size.width,size.height),SurfaceFormat::B8G8R8A8);if(dt){result.mSourceSurface=dt->Snapshot();}}elseif(aTarget){RefPtr<SourceSurface>opt=aTarget->OptimizeSourceSurface(result.mSourceSurface);if(opt){result.mSourceSurface=opt;}}result.mHasSize=true;result.mSize=size;result.mIsWriteOnly=aOffscreenCanvas->IsWriteOnly();returnresult;}nsLayoutUtils::SurfaceFromElementResultnsLayoutUtils::SurfaceFromElement(nsIImageLoadingContent*aElement,uint32_taSurfaceFlags,RefPtr<DrawTarget>&aTarget){SurfaceFromElementResultresult;nsresultrv;nsCOMPtr<imgIRequest>imgRequest;rv=aElement->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,getter_AddRefs(imgRequest));if(NS_FAILED(rv)){returnresult;}if(!imgRequest){// There's no image request. This is either because a request for// a non-empty URI failed, or the URI is the empty string.nsCOMPtr<nsIURI>currentURI;aElement->GetCurrentURI(getter_AddRefs(currentURI));if(!currentURI){// Treat the empty URI as available instead of broken state.result.mHasSize=true;}returnresult;}uint32_tstatus;imgRequest->GetImageStatus(&status);result.mHasSize=status&imgIRequest::STATUS_SIZE_AVAILABLE;if((status&imgIRequest::STATUS_LOAD_COMPLETE)==0){// Spec says to use GetComplete, but that only works on// nsIDOMHTMLImageElement, and we support all sorts of other stuff// here. Do this for now pending spec clarification.result.mIsStillLoading=(status&imgIRequest::STATUS_ERROR)==0;returnresult;}nsCOMPtr<nsIPrincipal>principal;rv=imgRequest->GetImagePrincipal(getter_AddRefs(principal));if(NS_FAILED(rv)){returnresult;}nsCOMPtr<imgIContainer>imgContainer;rv=imgRequest->GetImage(getter_AddRefs(imgContainer));if(NS_FAILED(rv)){returnresult;}uint32_tnoRasterize=aSurfaceFlags&SFE_NO_RASTERIZING_VECTORS;uint32_twhichFrame=(aSurfaceFlags&SFE_WANT_FIRST_FRAME_IF_IMAGE)?(uint32_t)imgIContainer::FRAME_FIRST:(uint32_t)imgIContainer::FRAME_CURRENT;uint32_tframeFlags=imgIContainer::FLAG_SYNC_DECODE|imgIContainer::FLAG_ASYNC_NOTIFY;if(aSurfaceFlags&SFE_NO_COLORSPACE_CONVERSION)frameFlags|=imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION;if(aSurfaceFlags&SFE_PREFER_NO_PREMULTIPLY_ALPHA){frameFlags|=imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;}int32_timgWidth,imgHeight;nsCOMPtr<nsIContent>content=do_QueryInterface(aElement);HTMLImageElement*element=HTMLImageElement::FromContentOrNull(content);if(aSurfaceFlags&SFE_USE_ELEMENT_SIZE_IF_VECTOR&&element&&imgContainer->GetType()==imgIContainer::TYPE_VECTOR){imgWidth=element->Width();imgHeight=element->Height();}else{rv=imgContainer->GetWidth(&imgWidth);nsresultrv2=imgContainer->GetHeight(&imgHeight);if(NS_FAILED(rv)||NS_FAILED(rv2))returnresult;}result.mSize=IntSize(imgWidth,imgHeight);if(!noRasterize||imgContainer->GetType()==imgIContainer::TYPE_RASTER){if(aSurfaceFlags&SFE_WANT_IMAGE_SURFACE){frameFlags|=imgIContainer::FLAG_WANT_DATA_SURFACE;}result.mSourceSurface=imgContainer->GetFrameAtSize(result.mSize,whichFrame,frameFlags);if(!result.mSourceSurface){returnresult;}// The surface we return is likely to be cached. We don't want to have to// convert to a surface that's compatible with aTarget each time it's used// (that would result in terrible performance), so we convert once here// upfront if aTarget is specified.if(aTarget){RefPtr<SourceSurface>optSurface=aTarget->OptimizeSourceSurface(result.mSourceSurface);if(optSurface){result.mSourceSurface=optSurface;}}constauto&format=result.mSourceSurface->GetFormat();if(IsOpaque(format)){result.mAlphaType=gfxAlphaType::Opaque;}elseif(frameFlags&imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA){result.mAlphaType=gfxAlphaType::NonPremult;}else{result.mAlphaType=gfxAlphaType::Premult;}}else{result.mDrawInfo.mImgContainer=imgContainer;result.mDrawInfo.mWhichFrame=whichFrame;result.mDrawInfo.mDrawingFlags=frameFlags;}int32_tcorsmode;if(NS_SUCCEEDED(imgRequest->GetCORSMode(&corsmode))){result.mCORSUsed=(corsmode!=imgIRequest::CORS_NONE);}result.mPrincipal=principal.forget();// no images, including SVG images, can load content from another domain.result.mIsWriteOnly=false;result.mImageRequest=imgRequest.forget();returnresult;}nsLayoutUtils::SurfaceFromElementResultnsLayoutUtils::SurfaceFromElement(HTMLImageElement*aElement,uint32_taSurfaceFlags,RefPtr<DrawTarget>&aTarget){returnSurfaceFromElement(static_cast<nsIImageLoadingContent*>(aElement),aSurfaceFlags,aTarget);}nsLayoutUtils::SurfaceFromElementResultnsLayoutUtils::SurfaceFromElement(HTMLCanvasElement*aElement,uint32_taSurfaceFlags,RefPtr<DrawTarget>&aTarget){SurfaceFromElementResultresult;IntSizesize=aElement->GetSize();result.mSourceSurface=aElement->GetSurfaceSnapshot(&result.mAlphaType);if(!result.mSourceSurface){// If the element doesn't have a context then we won't get a snapshot. The canvas spec wants us to not error and just// draw nothing, so return an empty surface.result.mAlphaType=gfxAlphaType::Opaque;DrawTarget*ref=aTarget?aTarget.get():gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();RefPtr<DrawTarget>dt=ref->CreateSimilarDrawTarget(IntSize(size.width,size.height),SurfaceFormat::B8G8R8A8);if(dt){result.mSourceSurface=dt->Snapshot();}}elseif(aTarget){RefPtr<SourceSurface>opt=aTarget->OptimizeSourceSurface(result.mSourceSurface);if(opt){result.mSourceSurface=opt;}}// Ensure that any future changes to the canvas trigger proper invalidation,// in case this is being used by -moz-element()aElement->MarkContextClean();result.mHasSize=true;result.mSize=size;result.mPrincipal=aElement->NodePrincipal();result.mIsWriteOnly=aElement->IsWriteOnly();returnresult;}nsLayoutUtils::SurfaceFromElementResultnsLayoutUtils::SurfaceFromElement(HTMLVideoElement*aElement,uint32_taSurfaceFlags,RefPtr<DrawTarget>&aTarget){SurfaceFromElementResultresult;result.mAlphaType=gfxAlphaType::Opaque;// Assume opaque.if(aElement->ContainsRestrictedContent()){returnresult;}uint16_treadyState;if(NS_SUCCEEDED(aElement->GetReadyState(&readyState))&&(readyState==nsIDOMHTMLMediaElement::HAVE_NOTHING||readyState==nsIDOMHTMLMediaElement::HAVE_METADATA)){result.mIsStillLoading=true;returnresult;}// If it doesn't have a principal, just bailnsCOMPtr<nsIPrincipal>principal=aElement->GetCurrentVideoPrincipal();if(!principal)returnresult;result.mLayersImage=aElement->GetCurrentImage();if(!result.mLayersImage)returnresult;if(aTarget){// They gave us a DrawTarget to optimize for, so even though we have a layers::Image,// we should unconditionally grab a SourceSurface and try to optimize it.result.mSourceSurface=result.mLayersImage->GetAsSourceSurface();if(!result.mSourceSurface)returnresult;RefPtr<SourceSurface>opt=aTarget->OptimizeSourceSurface(result.mSourceSurface);if(opt){result.mSourceSurface=opt;}}result.mCORSUsed=aElement->GetCORSMode()!=CORS_NONE;result.mHasSize=true;result.mSize=result.mLayersImage->GetSize();result.mPrincipal=principal.forget();result.mIsWriteOnly=false;returnresult;}nsLayoutUtils::SurfaceFromElementResultnsLayoutUtils::SurfaceFromElement(dom::Element*aElement,uint32_taSurfaceFlags,RefPtr<DrawTarget>&aTarget){// If it's a <canvas>, we may be able to just grab its internal surfaceif(HTMLCanvasElement*canvas=HTMLCanvasElement::FromContentOrNull(aElement)){returnSurfaceFromElement(canvas,aSurfaceFlags,aTarget);}// Maybe it's <video>?if(HTMLVideoElement*video=HTMLVideoElement::FromContentOrNull(aElement)){returnSurfaceFromElement(video,aSurfaceFlags,aTarget);}// Finally, check if it's a normal imagensCOMPtr<nsIImageLoadingContent>imageLoader=do_QueryInterface(aElement);if(!imageLoader){returnSurfaceFromElementResult();}returnSurfaceFromElement(imageLoader,aSurfaceFlags,aTarget);}/* static */nsIContent*nsLayoutUtils::GetEditableRootContentByContentEditable(nsIDocument*aDocument){// If the document is in designMode we should return nullptr.if(!aDocument||aDocument->HasFlag(NODE_IS_EDITABLE)){returnnullptr;}// contenteditable only works with HTML document.// Note: Use nsIDOMHTMLDocument rather than nsIHTMLDocument for getting the// body node because nsIDOMHTMLDocument::GetBody() does something// additional work for some cases and EditorBase uses them.nsCOMPtr<nsIDOMHTMLDocument>domHTMLDoc=do_QueryInterface(aDocument);if(!domHTMLDoc){returnnullptr;}Element*rootElement=aDocument->GetRootElement();if(rootElement&&rootElement->IsEditable()){returnrootElement;}// If there are no editable root element, check its <body> element.// Note that the body element could be <frameset> element.nsCOMPtr<nsIDOMHTMLElement>body;nsresultrv=domHTMLDoc->GetBody(getter_AddRefs(body));nsCOMPtr<nsIContent>content=do_QueryInterface(body);if(NS_SUCCEEDED(rv)&&content&&content->IsEditable()){returncontent;}returnnullptr;}#ifdef DEBUG/* static */voidnsLayoutUtils::AssertNoDuplicateContinuations(nsIFrame*aContainer,constnsFrameList&aFrameList){for(nsIFrame*f:aFrameList){// Check only later continuations of f; we deal with checking the// earlier continuations when we hit those earlier continuations in// the frame list.for(nsIFrame*c=f;(c=c->GetNextInFlow());){NS_ASSERTION(c->GetParent()!=aContainer||!aFrameList.ContainsFrame(c),"Two continuations of the same frame in the same ""frame list");}}}// Is one of aFrame's ancestors a letter frame?staticboolIsInLetterFrame(nsIFrame*aFrame){for(nsIFrame*f=aFrame->GetParent();f;f=f->GetParent()){if(f->IsLetterFrame()){returntrue;}}returnfalse;}/* static */voidnsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(nsIFrame*aSubtreeRoot){NS_ASSERTION(aSubtreeRoot->GetPrevInFlow(),"frame tree not empty, but caller reported complete status");// Also assert that text frames map no text.int32_tstart,end;nsresultrv=aSubtreeRoot->GetOffsets(start,end);NS_ASSERTION(NS_SUCCEEDED(rv),"GetOffsets failed");// In some cases involving :first-letter, we'll partially unlink a// continuation in the middle of a continuation chain from its// previous and next continuations before destroying it, presumably so// that we don't also destroy the later continuations. Once we've// done this, GetOffsets returns incorrect values.// For examples, see list of tests in// https://bugzilla.mozilla.org/show_bug.cgi?id=619021#c29NS_ASSERTION(start==end||IsInLetterFrame(aSubtreeRoot),"frame tree not empty, but caller reported complete status");nsIFrame::ChildListIteratorlists(aSubtreeRoot);for(;!lists.IsDone();lists.Next()){nsFrameList::EnumeratorchildFrames(lists.CurrentList());for(;!childFrames.AtEnd();childFrames.Next()){nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(childFrames.get());}}}#endifstaticvoidGetFontFacesForFramesInner(nsIFrame*aFrame,nsFontFaceList*aFontFaceList){NS_PRECONDITION(aFrame,"NULL frame pointer");if(aFrame->IsTextFrame()){if(!aFrame->GetPrevContinuation()){nsLayoutUtils::GetFontFacesForText(aFrame,0,INT32_MAX,true,aFontFaceList);}return;}nsIFrame::ChildListIDchildLists[]={nsIFrame::kPrincipalList,nsIFrame::kPopupList};for(size_ti=0;i<ArrayLength(childLists);++i){nsFrameListchildren(aFrame->GetChildList(childLists[i]));for(nsFrameList::Enumeratore(children);!e.AtEnd();e.Next()){nsIFrame*child=e.get();child=nsPlaceholderFrame::GetRealFrameFor(child);GetFontFacesForFramesInner(child,aFontFaceList);}}}/* static */nsresultnsLayoutUtils::GetFontFacesForFrames(nsIFrame*aFrame,nsFontFaceList*aFontFaceList){NS_PRECONDITION(aFrame,"NULL frame pointer");while(aFrame){GetFontFacesForFramesInner(aFrame,aFontFaceList);aFrame=GetNextContinuationOrIBSplitSibling(aFrame);}returnNS_OK;}/* static */nsresultnsLayoutUtils::GetFontFacesForText(nsIFrame*aFrame,int32_taStartOffset,int32_taEndOffset,boolaFollowContinuations,nsFontFaceList*aFontFaceList){NS_PRECONDITION(aFrame,"NULL frame pointer");if(!aFrame->IsTextFrame()){returnNS_OK;}nsTextFrame*curr=static_cast<nsTextFrame*>(aFrame);do{int32_tfstart=std::max(curr->GetContentOffset(),aStartOffset);int32_tfend=std::min(curr->GetContentEnd(),aEndOffset);if(fstart>=fend){curr=static_cast<nsTextFrame*>(curr->GetNextContinuation());continue;}// curr is overlapping with the offset we wantgfxSkipCharsIteratoriter=curr->EnsureTextRun(nsTextFrame::eInflated);gfxTextRun*textRun=curr->GetTextRun(nsTextFrame::eInflated);NS_ENSURE_TRUE(textRun,NS_ERROR_OUT_OF_MEMORY);// include continuations in the range that share the same textrunnsTextFrame*next=nullptr;if(aFollowContinuations&&fend<aEndOffset){next=static_cast<nsTextFrame*>(curr->GetNextContinuation());while(next&&next->GetTextRun(nsTextFrame::eInflated)==textRun){fend=std::min(next->GetContentEnd(),aEndOffset);next=fend<aEndOffset?static_cast<nsTextFrame*>(next->GetNextContinuation()):nullptr;}}uint32_tskipStart=iter.ConvertOriginalToSkipped(fstart);uint32_tskipEnd=iter.ConvertOriginalToSkipped(fend);aFontFaceList->AddFontsFromTextRun(textRun,skipStart,skipEnd-skipStart);curr=next;}while(aFollowContinuations&&curr);returnNS_OK;}/* static */size_tnsLayoutUtils::SizeOfTextRunsForFrames(nsIFrame*aFrame,MallocSizeOfaMallocSizeOf,boolclear){NS_PRECONDITION(aFrame,"NULL frame pointer");size_ttotal=0;if(aFrame->IsTextFrame()){nsTextFrame*textFrame=static_cast<nsTextFrame*>(aFrame);for(uint32_ti=0;i<2;++i){gfxTextRun*run=textFrame->GetTextRun((i!=0)?nsTextFrame::eInflated:nsTextFrame::eNotInflated);if(run){if(clear){run->ResetSizeOfAccountingFlags();}else{total+=run->MaybeSizeOfIncludingThis(aMallocSizeOf);}}}returntotal;}AutoTArray<nsIFrame::ChildList,4>childListArray;aFrame->GetChildLists(&childListArray);for(nsIFrame::ChildListArrayIteratorchildLists(childListArray);!childLists.IsDone();childLists.Next()){for(nsFrameList::Enumeratore(childLists.CurrentList());!e.AtEnd();e.Next()){total+=SizeOfTextRunsForFrames(e.get(),aMallocSizeOf,clear);}}returntotal;}structPrefCallbacks{constchar*name;PrefChangedFuncfunc;};staticconstPrefCallbackskPrefCallbacks[]={{GRID_ENABLED_PREF_NAME,GridEnabledPrefChangeCallback},{WEBKIT_PREFIXES_ENABLED_PREF_NAME,WebkitPrefixEnabledPrefChangeCallback},{TEXT_ALIGN_UNSAFE_ENABLED_PREF_NAME,TextAlignUnsafeEnabledPrefChangeCallback},{FLOAT_LOGICAL_VALUES_ENABLED_PREF_NAME,FloatLogicalValuesEnabledPrefChangeCallback},};/* static */voidnsLayoutUtils::Initialize(){Preferences::AddUintVarCache(&sFontSizeInflationMaxRatio,"font.size.inflation.maxRatio");Preferences::AddUintVarCache(&sFontSizeInflationEmPerLine,"font.size.inflation.emPerLine");Preferences::AddUintVarCache(&sFontSizeInflationMinTwips,"font.size.inflation.minTwips");Preferences::AddUintVarCache(&sFontSizeInflationLineThreshold,"font.size.inflation.lineThreshold");Preferences::AddIntVarCache(&sFontSizeInflationMappingIntercept,"font.size.inflation.mappingIntercept");Preferences::AddBoolVarCache(&sFontSizeInflationForceEnabled,"font.size.inflation.forceEnabled");Preferences::AddBoolVarCache(&sFontSizeInflationDisabledInMasterProcess,"font.size.inflation.disabledInMasterProcess");Preferences::AddUintVarCache(&sSystemFontScale,"font.size.systemFontScale",100);Preferences::AddUintVarCache(&sZoomMaxPercent,"zoom.maxPercent",300);Preferences::AddUintVarCache(&sZoomMinPercent,"zoom.minPercent",30);Preferences::AddBoolVarCache(&sInvalidationDebuggingIsEnabled,"nglayout.debug.invalidation");Preferences::AddBoolVarCache(&sInterruptibleReflowEnabled,"layout.interruptible-reflow.enabled");Preferences::AddBoolVarCache(&sSVGTransformBoxEnabled,"svg.transform-box.enabled");Preferences::AddBoolVarCache(&sTextCombineUprightDigitsEnabled,"layout.css.text-combine-upright-digits.enabled");#ifdef MOZ_STYLOPreferences::AddBoolVarCache(&sStyloEnabled,"layout.css.servo.enabled");#endifPreferences::AddBoolVarCache(&sStyleAttrWithXMLBaseDisabled,"layout.css.style-attr-with-xml-base.disabled");Preferences::AddUintVarCache(&sIdlePeriodDeadlineLimit,"layout.idle_period.time_limit",DEFAULT_IDLE_PERIOD_TIME_LIMIT);Preferences::AddUintVarCache(&sQuiescentFramesBeforeIdlePeriod,"layout.idle_period.required_quiescent_frames",DEFAULT_QUIESCENT_FRAMES);for(auto&callback:kPrefCallbacks){Preferences::RegisterCallbackAndCall(callback.func,callback.name);}nsComputedDOMStyle::RegisterPrefChangeCallbacks();}/* static */voidnsLayoutUtils::Shutdown(){if(sContentMap){deletesContentMap;sContentMap=nullptr;}for(auto&callback:kPrefCallbacks){Preferences::UnregisterCallback(callback.func,callback.name);}nsComputedDOMStyle::UnregisterPrefChangeCallbacks();// so the cached initial quotes array doesn't appear to be a leaknsStyleList::Shutdown();}/* static */voidnsLayoutUtils::RegisterImageRequest(nsPresContext*aPresContext,imgIRequest*aRequest,bool*aRequestRegistered){if(!aPresContext){return;}if(aRequestRegistered&&*aRequestRegistered){// Our request is already registered with the refresh driver, so// no need to register it again.return;}if(aRequest){if(!aPresContext->RefreshDriver()->AddImageRequest(aRequest)){NS_WARNING("Unable to add image request");return;}if(aRequestRegistered){*aRequestRegistered=true;}}}/* static */voidnsLayoutUtils::RegisterImageRequestIfAnimated(nsPresContext*aPresContext,imgIRequest*aRequest,bool*aRequestRegistered){if(!aPresContext){return;}if(aRequestRegistered&&*aRequestRegistered){// Our request is already registered with the refresh driver, so// no need to register it again.return;}if(aRequest){nsCOMPtr<imgIContainer>image;if(NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))){// Check to verify that the image is animated. If so, then add it to the// list of images tracked by the refresh driver.boolisAnimated=false;nsresultrv=image->GetAnimated(&isAnimated);if(NS_SUCCEEDED(rv)&&isAnimated){if(!aPresContext->RefreshDriver()->AddImageRequest(aRequest)){NS_WARNING("Unable to add image request");return;}if(aRequestRegistered){*aRequestRegistered=true;}}}}}/* static */voidnsLayoutUtils::DeregisterImageRequest(nsPresContext*aPresContext,imgIRequest*aRequest,bool*aRequestRegistered){if(!aPresContext){return;}// Deregister our imgIRequest with the refresh driver to// complete tear-down, but only if it has been registeredif(aRequestRegistered&&!*aRequestRegistered){return;}if(aRequest){nsCOMPtr<imgIContainer>image;if(NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))){aPresContext->RefreshDriver()->RemoveImageRequest(aRequest);if(aRequestRegistered){*aRequestRegistered=false;}}}}/* static */voidnsLayoutUtils::PostRestyleEvent(Element*aElement,nsRestyleHintaRestyleHint,nsChangeHintaMinChangeHint){nsIDocument*doc=aElement->GetComposedDoc();if(doc){nsCOMPtr<nsIPresShell>presShell=doc->GetShell();if(presShell){presShell->GetPresContext()->RestyleManager()->PostRestyleEvent(aElement,aRestyleHint,aMinChangeHint);}}}nsSetAttrRunnable::nsSetAttrRunnable(nsIContent*aContent,nsIAtom*aAttrName,constnsAString&aValue):mozilla::Runnable("nsSetAttrRunnable"),mContent(aContent),mAttrName(aAttrName),mValue(aValue){NS_ASSERTION(aContent&&aAttrName,"Missing stuff, prepare to crash");}nsSetAttrRunnable::nsSetAttrRunnable(nsIContent*aContent,nsIAtom*aAttrName,int32_taValue):mozilla::Runnable("nsSetAttrRunnable"),mContent(aContent),mAttrName(aAttrName){NS_ASSERTION(aContent&&aAttrName,"Missing stuff, prepare to crash");mValue.AppendInt(aValue);}NS_IMETHODIMPnsSetAttrRunnable::Run(){returnmContent->SetAttr(kNameSpaceID_None,mAttrName,mValue,true);}nsUnsetAttrRunnable::nsUnsetAttrRunnable(nsIContent*aContent,nsIAtom*aAttrName):mozilla::Runnable("nsUnsetAttrRunnable"),mContent(aContent),mAttrName(aAttrName){NS_ASSERTION(aContent&&aAttrName,"Missing stuff, prepare to crash");}NS_IMETHODIMPnsUnsetAttrRunnable::Run(){returnmContent->UnsetAttr(kNameSpaceID_None,mAttrName,true);}/** * Compute the minimum font size inside of a container with the given * width, such that **when the user zooms the container to fill the full * width of the device**, the fonts satisfy our minima. */staticnscoordMinimumFontSizeFor(nsPresContext*aPresContext,WritingModeaWritingMode,nscoordaContainerISize){nsIPresShell*presShell=aPresContext->PresShell();uint32_temPerLine=presShell->FontSizeInflationEmPerLine();uint32_tminTwips=presShell->FontSizeInflationMinTwips();if(emPerLine==0&&minTwips==0){return0;}// Clamp the container width to the device dimensionsnscoordiFrameISize=aWritingMode.IsVertical()?aPresContext->GetVisibleArea().height:aPresContext->GetVisibleArea().width;nscoordeffectiveContainerISize=std::min(iFrameISize,aContainerISize);nscoordbyLine=0,byInch=0;if(emPerLine!=0){byLine=effectiveContainerISize/emPerLine;}if(minTwips!=0){// REVIEW: Is this giving us app units and sizes *not* counting// viewport scaling?gfxSizescreenSize=aPresContext->ScreenSizeInchesForFontInflation();floatdeviceISizeInches=aWritingMode.IsVertical()?screenSize.height:screenSize.width;byInch=NSToCoordRound(effectiveContainerISize/(deviceISizeInches*1440/minTwips));}returnstd::max(byLine,byInch);}/* static */floatnsLayoutUtils::FontSizeInflationInner(constnsIFrame*aFrame,nscoordaMinFontSize){// Note that line heights should be inflated by the same ratio as the// font size of the same text; thus we operate only on the font size// even when we're scaling a line height.nscoordstyleFontSize=aFrame->StyleFont()->mFont.size;if(styleFontSize<=0){// Never scale zero font size.return1.0;}if(aMinFontSize<=0){// No need to scale.return1.0;}// If between this current frame and its font inflation container there is a// non-inline element with fixed width or height, then we should not inflate// fonts for this frame.for(constnsIFrame*f=aFrame;f&&!f->IsContainerForFontSizeInflation();f=f->GetParent()){nsIContent*content=f->GetContent();LayoutFrameTypefType=f->Type();nsIFrame*parent=f->GetParent();// Also, if there is more than one frame corresponding to a single// content node, we want the outermost one.if(!(parent&&parent->GetContent()==content)&&// ignore width/height on inlines since they don't applyfType!=LayoutFrameType::Inline&&// ignore width on radios and checkboxes since we enlarge them and// they have width/height in ua.cssfType!=LayoutFrameType::FormControl){// ruby annotations should have the same inflation as its// grandparent, which is the ruby frame contains the annotation.if(fType==LayoutFrameType::RubyText){MOZ_ASSERT(parent&&parent->IsRubyTextContainerFrame());nsIFrame*grandparent=parent->GetParent();MOZ_ASSERT(grandparent&&grandparent->IsRubyFrame());returnFontSizeInflationFor(grandparent);}nsStyleCoordstylePosWidth=f->StylePosition()->mWidth;nsStyleCoordstylePosHeight=f->StylePosition()->mHeight;if(stylePosWidth.GetUnit()!=eStyleUnit_Auto||stylePosHeight.GetUnit()!=eStyleUnit_Auto){return1.0;}}}int32_tinterceptParam=nsLayoutUtils::FontSizeInflationMappingIntercept();floatmaxRatio=(float)nsLayoutUtils::FontSizeInflationMaxRatio()/100.0f;floatratio=float(styleFontSize)/float(aMinFontSize);floatinflationRatio;// Given a minimum inflated font size m, a specified font size s, we want to// find the inflated font size i and then return the ratio of i to s (i/s).if(interceptParam>=0){// Since the mapping intercept parameter P is greater than zero, we use it// to determine the point where our mapping function intersects the i=s// line. This means that we have an equation of the form://// i = m + s·(P/2)/(1 + P/2), if s <= (1 + P/2)·m// i = s, if s >= (1 + P/2)·mfloatintercept=1+float(interceptParam)/2.0f;if(ratio>=intercept){// If we're already at 1+P/2 or more times the minimum, don't scale.return1.0;}// The point (intercept, intercept) is where the part of the i vs. s graph// that's not slope 1 meets the i=s line. (This part of the// graph is a line from (0, m), to that point). We calculate the// intersection point to be ((1+P/2)m, (1+P/2)m), where P is the// intercept parameter above. We then need to return i/s.inflationRatio=(1.0f+(ratio*(intercept-1)/intercept))/ratio;}else{// This is the case where P is negative. We essentially want to implement// the case for P=infinity here, so we make i = s + m, which means that// i/s = s/s + m/s = 1 + 1/ratioinflationRatio=1+1.0f/ratio;}if(maxRatio>1.0&&inflationRatio>maxRatio){returnmaxRatio;}else{returninflationRatio;}}staticboolShouldInflateFontsForContainer(constnsIFrame*aFrame){// We only want to inflate fonts for text that is in a place// with room to expand. The question is what the best heuristic for// that is...// For now, we're going to use NS_FRAME_IN_CONSTRAINED_BSIZE, which// indicates whether the frame is inside something with a constrained// block-size (propagating down the tree), but the propagation stops when// we hit overflow-y [or -x, for vertical mode]: scroll or auto.constnsStyleText*styleText=aFrame->StyleText();returnstyleText->mTextSizeAdjust!=NS_STYLE_TEXT_SIZE_ADJUST_NONE&&!(aFrame->GetStateBits()&NS_FRAME_IN_CONSTRAINED_BSIZE)&&// We also want to disable font inflation for containers that have// preformatted text.// MathML cells need special treatment. See bug 1002526 comment 56.(styleText->WhiteSpaceCanWrap(aFrame)||aFrame->IsFrameOfType(nsIFrame::eMathML));}nscoordnsLayoutUtils::InflationMinFontSizeFor(constnsIFrame*aFrame){nsPresContext*presContext=aFrame->PresContext();if(!FontSizeInflationEnabled(presContext)||presContext->mInflationDisabledForShrinkWrap){return0;}for(constnsIFrame*f=aFrame;f;f=f->GetParent()){if(f->IsContainerForFontSizeInflation()){if(!ShouldInflateFontsForContainer(f)){return0;}nsFontInflationData*data=nsFontInflationData::FindFontInflationDataFor(aFrame);// FIXME: The need to null-check here is sort of a bug, and might// lead to incorrect results.if(!data||!data->InflationEnabled()){return0;}returnMinimumFontSizeFor(aFrame->PresContext(),aFrame->GetWritingMode(),data->EffectiveISize());}}MOZ_ASSERT(false,"root should always be container");return0;}floatnsLayoutUtils::FontSizeInflationFor(constnsIFrame*aFrame){if(nsSVGUtils::IsInSVGTextSubtree(aFrame)){constnsIFrame*container=aFrame;while(!container->IsSVGTextFrame()){container=container->GetParent();}NS_ASSERTION(container,"expected to find an ancestor SVGTextFrame");returnstatic_cast<constSVGTextFrame*>(container)->GetFontSizeScaleFactor();}if(!FontSizeInflationEnabled(aFrame->PresContext())){return1.0f;}returnFontSizeInflationInner(aFrame,InflationMinFontSizeFor(aFrame));}/* static */boolnsLayoutUtils::FontSizeInflationEnabled(nsPresContext*aPresContext){nsIPresShell*presShell=aPresContext->GetPresShell();if(!presShell){returnfalse;}returnpresShell->FontSizeInflationEnabled();}/* static */nsRectnsLayoutUtils::GetBoxShadowRectForFrame(nsIFrame*aFrame,constnsSize&aFrameSize){nsCSSShadowArray*boxShadows=aFrame->StyleEffects()->mBoxShadow;if(!boxShadows){returnnsRect();}boolnativeTheme;constnsStyleDisplay*styleDisplay=aFrame->StyleDisplay();nsITheme::Transparencytransparency;if(aFrame->IsThemed(styleDisplay,&transparency)){// For opaque (rectangular) theme widgets we can take the generic// border-box path with border-radius disabled.nativeTheme=transparency!=nsITheme::eOpaque;}else{nativeTheme=false;}nsRectframeRect=nativeTheme?aFrame->GetVisualOverflowRectRelativeToSelf():nsRect(nsPoint(0,0),aFrameSize);nsRectshadows;int32_tA2D=aFrame->PresContext()->AppUnitsPerDevPixel();for(uint32_ti=0;i<boxShadows->Length();++i){nsRecttmpRect=frameRect;nsCSSShadowItem*shadow=boxShadows->ShadowAt(i);// inset shadows are never painted outside the frameif(shadow->mInset)continue;tmpRect.MoveBy(nsPoint(shadow->mXOffset,shadow->mYOffset));tmpRect.Inflate(shadow->mSpread);tmpRect.Inflate(nsContextBoxBlur::GetBlurRadiusMargin(shadow->mRadius,A2D));shadows.UnionRect(shadows,tmpRect);}returnshadows;}/* static */boolnsLayoutUtils::GetContentViewerSize(nsPresContext*aPresContext,LayoutDeviceIntSize&aOutSize){nsCOMPtr<nsIDocShell>docShell=aPresContext->GetDocShell();if(!docShell){returnfalse;}nsCOMPtr<nsIContentViewer>cv;docShell->GetContentViewer(getter_AddRefs(cv));if(!cv){returnfalse;}nsIntRectbounds;cv->GetBounds(bounds);aOutSize=LayoutDeviceIntRect::FromUnknownRect(bounds).Size();returntrue;}staticboolUpdateCompositionBoundsForRCDRSF(ParentLayerRect&aCompBounds,nsPresContext*aPresContext,boolaScaleContentViewerSize){nsIFrame*rootFrame=aPresContext->PresShell()->GetRootFrame();if(!rootFrame){returnfalse;}#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT)nsIWidget*widget=rootFrame->GetNearestWidget();#elsensView*view=rootFrame->GetView();nsIWidget*widget=view?view->GetWidget():nullptr;#endifif(widget){LayoutDeviceIntRectwidgetBounds=widget->GetBounds();widgetBounds.MoveTo(0,0);aCompBounds=ParentLayerRect(ViewAs<ParentLayerPixel>(widgetBounds,PixelCastJustification::LayoutDeviceIsParentLayerForRCDRSF));returntrue;}LayoutDeviceIntSizecontentSize;if(nsLayoutUtils::GetContentViewerSize(aPresContext,contentSize)){LayoutDeviceToParentLayerScalescale;if(aScaleContentViewerSize&&aPresContext->GetParentPresContext()){scale=LayoutDeviceToParentLayerScale(aPresContext->GetParentPresContext()->PresShell()->GetCumulativeResolution());}aCompBounds.SizeTo(contentSize*scale);returntrue;}returnfalse;}/* static */nsMarginnsLayoutUtils::ScrollbarAreaToExcludeFromCompositionBoundsFor(nsIFrame*aScrollFrame){if(!aScrollFrame||!aScrollFrame->GetScrollTargetFrame()){returnnsMargin();}nsPresContext*presContext=aScrollFrame->PresContext();nsIPresShell*presShell=presContext->GetPresShell();if(!presShell){returnnsMargin();}boolisRootScrollFrame=aScrollFrame==presShell->GetRootScrollFrame();boolisRootContentDocRootScrollFrame=isRootScrollFrame&&presContext->IsRootContentDocument();if(!isRootContentDocRootScrollFrame){returnnsMargin();}if(LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)){returnnsMargin();}nsIScrollableFrame*scrollableFrame=aScrollFrame->GetScrollTargetFrame();if(!scrollableFrame){returnnsMargin();}returnscrollableFrame->GetActualScrollbarSizes();}/* static */nsSizensLayoutUtils::CalculateCompositionSizeForFrame(nsIFrame*aFrame,boolaSubtractScrollbars){// If we have a scrollable frame, restrict the composition bounds to its// scroll port. The scroll port excludes the frame borders and the scroll// bars, which we don't want to be part of the composition bounds.nsIScrollableFrame*scrollableFrame=aFrame->GetScrollTargetFrame();nsRectrect=scrollableFrame?scrollableFrame->GetScrollPortRect():aFrame->GetRect();nsSizesize=rect.Size();nsPresContext*presContext=aFrame->PresContext();nsIPresShell*presShell=presContext->PresShell();boolisRootContentDocRootScrollFrame=presContext->IsRootContentDocument()&&aFrame==presShell->GetRootScrollFrame();if(isRootContentDocRootScrollFrame){ParentLayerRectcompBounds;if(UpdateCompositionBoundsForRCDRSF(compBounds,presContext,false)){int32_tauPerDevPixel=presContext->AppUnitsPerDevPixel();size=nsSize(compBounds.width*auPerDevPixel,compBounds.height*auPerDevPixel);}}if(aSubtractScrollbars){nsMarginmargins=ScrollbarAreaToExcludeFromCompositionBoundsFor(aFrame);size.width-=margins.LeftRight();size.height-=margins.TopBottom();}returnsize;}/* static */CSSSizensLayoutUtils::CalculateRootCompositionSize(nsIFrame*aFrame,boolaIsRootContentDocRootScrollFrame,constFrameMetrics&aMetrics){if(aIsRootContentDocRootScrollFrame){returnViewAs<LayerPixel>(aMetrics.GetCompositionBounds().Size(),PixelCastJustification::ParentLayerToLayerForRootComposition)*LayerToScreenScale(1.0f)/aMetrics.DisplayportPixelsPerCSSPixel();}nsPresContext*presContext=aFrame->PresContext();ScreenSizerootCompositionSize;nsPresContext*rootPresContext=presContext->GetToplevelContentDocumentPresContext();if(!rootPresContext){rootPresContext=presContext->GetRootPresContext();}nsIPresShell*rootPresShell=nullptr;if(rootPresContext){rootPresShell=rootPresContext->PresShell();if(nsIFrame*rootFrame=rootPresShell->GetRootFrame()){LayoutDeviceToLayerScale2DcumulativeResolution(rootPresShell->GetCumulativeResolution()*nsLayoutUtils::GetTransformToAncestorScale(rootFrame));ParentLayerRectcompBounds;if(UpdateCompositionBoundsForRCDRSF(compBounds,rootPresContext,true)){rootCompositionSize=ViewAs<ScreenPixel>(compBounds.Size(),PixelCastJustification::ScreenIsParentLayerForRoot);}else{int32_trootAUPerDevPixel=rootPresContext->AppUnitsPerDevPixel();LayerSizeframeSize=(LayoutDeviceRect::FromAppUnits(rootFrame->GetRect(),rootAUPerDevPixel)*cumulativeResolution).Size();rootCompositionSize=frameSize*LayerToScreenScale(1.0f);}}}else{nsIWidget*widget=aFrame->GetNearestWidget();LayoutDeviceIntRectwidgetBounds=widget->GetBounds();rootCompositionSize=ScreenSize(ViewAs<ScreenPixel>(widgetBounds.Size(),PixelCastJustification::LayoutDeviceIsScreenForBounds));}// Adjust composition size for the size of scroll bars.nsIFrame*rootRootScrollFrame=rootPresShell?rootPresShell->GetRootScrollFrame():nullptr;nsMarginscrollbarMargins=ScrollbarAreaToExcludeFromCompositionBoundsFor(rootRootScrollFrame);LayoutDeviceMarginmargins=LayoutDeviceMargin::FromAppUnits(scrollbarMargins,rootPresContext->AppUnitsPerDevPixel());// Scrollbars are not subject to resolution scaling, so LD pixels = layer pixels for them.rootCompositionSize.width-=margins.LeftRight();rootCompositionSize.height-=margins.TopBottom();returnrootCompositionSize/aMetrics.DisplayportPixelsPerCSSPixel();}/* static */nsRectnsLayoutUtils::CalculateScrollableRectForFrame(nsIScrollableFrame*aScrollableFrame,nsIFrame*aRootFrame){nsRectcontentBounds;if(aScrollableFrame){contentBounds=aScrollableFrame->GetScrollRange();nsPointscrollPosition=aScrollableFrame->GetScrollPosition();if(aScrollableFrame->GetScrollbarStyles().mVertical==NS_STYLE_OVERFLOW_HIDDEN){contentBounds.y=scrollPosition.y;contentBounds.height=0;}if(aScrollableFrame->GetScrollbarStyles().mHorizontal==NS_STYLE_OVERFLOW_HIDDEN){contentBounds.x=scrollPosition.x;contentBounds.width=0;}contentBounds.width+=aScrollableFrame->GetScrollPortRect().width;contentBounds.height+=aScrollableFrame->GetScrollPortRect().height;}else{contentBounds=aRootFrame->GetRect();}returncontentBounds;}/* static */nsRectnsLayoutUtils::CalculateExpandedScrollableRect(nsIFrame*aFrame){nsRectscrollableRect=CalculateScrollableRectForFrame(aFrame->GetScrollTargetFrame(),aFrame->PresContext()->PresShell()->GetRootFrame());nsSizecompSize=CalculateCompositionSizeForFrame(aFrame);if(aFrame==aFrame->PresContext()->PresShell()->GetRootScrollFrame()){// the composition size for the root scroll frame does not include the// local resolution, so we adjust.floatres=aFrame->PresContext()->PresShell()->GetResolution();compSize.width=NSToCoordRound(compSize.width/res);compSize.height=NSToCoordRound(compSize.height/res);}if(scrollableRect.width<compSize.width){scrollableRect.x=std::max(0,scrollableRect.x-(compSize.width-scrollableRect.width));scrollableRect.width=compSize.width;}if(scrollableRect.height<compSize.height){scrollableRect.y=std::max(0,scrollableRect.y-(compSize.height-scrollableRect.height));scrollableRect.height=compSize.height;}returnscrollableRect;}/* static */voidnsLayoutUtils::DoLogTestDataForPaint(LayerManager*aManager,ViewIDaScrollId,conststd::string&aKey,conststd::string&aValue){if(ClientLayerManager*mgr=aManager->AsClientLayerManager()){mgr->LogTestDataForCurrentPaint(aScrollId,aKey,aValue);}elseif(WebRenderLayerManager*wrlm=aManager->AsWebRenderLayerManager()){wrlm->LogTestDataForCurrentPaint(aScrollId,aKey,aValue);}}/* static */boolnsLayoutUtils::IsAPZTestLoggingEnabled(){returngfxPrefs::APZTestLoggingEnabled();}////////////////////////////////////////// SurfaceFromElementResultnsLayoutUtils::SurfaceFromElementResult::SurfaceFromElementResult()// Use safe default values here:mIsWriteOnly(true),mIsStillLoading(false),mHasSize(false),mCORSUsed(false),mAlphaType(gfxAlphaType::Opaque){}constRefPtr<mozilla::gfx::SourceSurface>&nsLayoutUtils::SurfaceFromElementResult::GetSourceSurface(){if(!mSourceSurface&&mLayersImage){mSourceSurface=mLayersImage->GetAsSourceSurface();}returnmSourceSurface;}////////////////////////////////////////boolnsLayoutUtils::IsNonWrapperBlock(nsIFrame*aFrame){returnGetAsBlock(aFrame)&&!aFrame->IsBlockWrapper();}boolnsLayoutUtils::NeedsPrintPreviewBackground(nsPresContext*aPresContext){returnaPresContext->IsRootPaginatedDocument()&&(aPresContext->Type()==nsPresContext::eContext_PrintPreview||aPresContext->Type()==nsPresContext::eContext_PageLayout);}AutoMaybeDisableFontInflation::AutoMaybeDisableFontInflation(nsIFrame*aFrame){// FIXME: Now that inflation calculations are based on the flow// root's NCA's (nearest common ancestor of its inflatable// descendants) width, we could probably disable inflation in// fewer cases than we currently do.// MathML cells need special treatment. See bug 1002526 comment 56.if(aFrame->IsContainerForFontSizeInflation()&&!aFrame->IsFrameOfType(nsIFrame::eMathML)){mPresContext=aFrame->PresContext();mOldValue=mPresContext->mInflationDisabledForShrinkWrap;mPresContext->mInflationDisabledForShrinkWrap=true;}else{// indicate we have nothing to restoremPresContext=nullptr;}}AutoMaybeDisableFontInflation::~AutoMaybeDisableFontInflation(){if(mPresContext){mPresContext->mInflationDisabledForShrinkWrap=mOldValue;}}namespacemozilla{RectNSRectToRect(constnsRect&aRect,doubleaAppUnitsPerPixel){// Note that by making aAppUnitsPerPixel a double we're doing floating-point// division using a larger type and avoiding rounding error.returnRect(Float(aRect.x/aAppUnitsPerPixel),Float(aRect.y/aAppUnitsPerPixel),Float(aRect.width/aAppUnitsPerPixel),Float(aRect.height/aAppUnitsPerPixel));}RectNSRectToSnappedRect(constnsRect&aRect,doubleaAppUnitsPerPixel,constgfx::DrawTarget&aSnapDT){// Note that by making aAppUnitsPerPixel a double we're doing floating-point// division using a larger type and avoiding rounding error.Rectrect(Float(aRect.x/aAppUnitsPerPixel),Float(aRect.y/aAppUnitsPerPixel),Float(aRect.width/aAppUnitsPerPixel),Float(aRect.height/aAppUnitsPerPixel));MaybeSnapToDevicePixels(rect,aSnapDT,true);returnrect;}// Similar to a snapped rect, except an axis is left unsnapped if the snapping// process results in a length of 0.RectNSRectToNonEmptySnappedRect(constnsRect&aRect,doubleaAppUnitsPerPixel,constgfx::DrawTarget&aSnapDT){// Note that by making aAppUnitsPerPixel a double we're doing floating-point// division using a larger type and avoiding rounding error.Rectrect(Float(aRect.x/aAppUnitsPerPixel),Float(aRect.y/aAppUnitsPerPixel),Float(aRect.width/aAppUnitsPerPixel),Float(aRect.height/aAppUnitsPerPixel));MaybeSnapToDevicePixels(rect,aSnapDT,true,false);returnrect;}voidStrokeLineWithSnapping(constnsPoint&aP1,constnsPoint&aP2,int32_taAppUnitsPerDevPixel,DrawTarget&aDrawTarget,constPattern&aPattern,constStrokeOptions&aStrokeOptions,constDrawOptions&aDrawOptions){Pointp1=NSPointToPoint(aP1,aAppUnitsPerDevPixel);Pointp2=NSPointToPoint(aP2,aAppUnitsPerDevPixel);SnapLineToDevicePixelsForStroking(p1,p2,aDrawTarget,aStrokeOptions.mLineWidth);aDrawTarget.StrokeLine(p1,p2,aPattern,aStrokeOptions,aDrawOptions);}namespacelayout{voidMaybeSetupTransactionIdAllocator(layers::LayerManager*aManager,nsPresContext*aPresContext){autobackendType=aManager->GetBackendType();if(backendType==LayersBackend::LAYERS_CLIENT||backendType==LayersBackend::LAYERS_WR){aManager->SetTransactionIdAllocator(aPresContext->RefreshDriver());}}}// namespace layout}// namespace mozilla/* static */boolnsLayoutUtils::IsOutlineStyleAutoEnabled(){staticboolsOutlineStyleAutoEnabled;staticboolsOutlineStyleAutoPrefCached=false;if(!sOutlineStyleAutoPrefCached){sOutlineStyleAutoPrefCached=true;Preferences::AddBoolVarCache(&sOutlineStyleAutoEnabled,"layout.css.outline-style-auto.enabled",false);}returnsOutlineStyleAutoEnabled;}/* static */voidnsLayoutUtils::SetBSizeFromFontMetrics(constnsIFrame*aFrame,ReflowOutput&aMetrics,constLogicalMargin&aFramePadding,WritingModeaLineWM,WritingModeaFrameWM){RefPtr<nsFontMetrics>fm=nsLayoutUtils::GetInflatedFontMetricsForFrame(aFrame);if(fm){// Compute final height of the frame.//// Do things the standard css2 way -- though it's hard to find it// in the css2 spec! It's actually found in the css1 spec section// 4.4 (you will have to read between the lines to really see// it).//// The height of our box is the sum of our font size plus the top// and bottom border and padding. The height of children do not// affect our height.aMetrics.SetBlockStartAscent(aLineWM.IsLineInverted()?fm->MaxDescent():fm->MaxAscent());aMetrics.BSize(aLineWM)=fm->MaxHeight();}else{NS_WARNING("Cannot get font metrics - defaulting sizes to 0");aMetrics.SetBlockStartAscent(aMetrics.BSize(aLineWM)=0);}aMetrics.SetBlockStartAscent(aMetrics.BlockStartAscent()+aFramePadding.BStart(aFrameWM));aMetrics.BSize(aLineWM)+=aFramePadding.BStartEnd(aFrameWM);}/* static */boolnsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(nsIPresShell*aShell){if(nsIDocument*doc=aShell->GetDocument()){WidgetEventevent(true,eVoidEvent);nsTArray<EventTarget*>targets;nsresultrv=EventDispatcher::Dispatch(doc,nullptr,&event,nullptr,nullptr,nullptr,&targets);NS_ENSURE_SUCCESS(rv,false);for(size_ti=0;i<targets.Length();i++){if(targets[i]->IsApzAware()){returntrue;}}}returnfalse;}staticvoidMaybeReflowForInflationScreenSizeChange(nsPresContext*aPresContext){if(aPresContext){nsIPresShell*presShell=aPresContext->GetPresShell();boolfontInflationWasEnabled=presShell->FontSizeInflationEnabled();presShell->NotifyFontSizeInflationEnabledIsDirty();boolchanged=false;if(presShell&&presShell->FontSizeInflationEnabled()&&presShell->FontSizeInflationMinTwips()!=0){aPresContext->ScreenSizeInchesForFontInflation(&changed);}changed=changed||(fontInflationWasEnabled!=presShell->FontSizeInflationEnabled());if(changed){nsCOMPtr<nsIDocShell>docShell=aPresContext->GetDocShell();if(docShell){nsCOMPtr<nsIContentViewer>cv;docShell->GetContentViewer(getter_AddRefs(cv));if(cv){nsTArray<nsCOMPtr<nsIContentViewer>>array;cv->AppendSubtree(array);for(uint32_ti=0,iEnd=array.Length();i<iEnd;++i){nsCOMPtr<nsIPresShell>shell;nsCOMPtr<nsIContentViewer>cv=array[i];cv->GetPresShell(getter_AddRefs(shell));if(shell){nsIFrame*rootFrame=shell->GetRootFrame();if(rootFrame){shell->FrameNeedsReflow(rootFrame,nsIPresShell::eStyleChange,NS_FRAME_IS_DIRTY);}}}}}}}}/* static */voidnsLayoutUtils::SetScrollPositionClampingScrollPortSize(nsIPresShell*aPresShell,CSSSizeaSize){MOZ_ASSERT(aSize.width>=0.0&&aSize.height>=0.0);aPresShell->SetScrollPositionClampingScrollPortSize(nsPresContext::CSSPixelsToAppUnits(aSize.width),nsPresContext::CSSPixelsToAppUnits(aSize.height));// When the "font.size.inflation.minTwips" preference is set, the// layout depends on the size of the screen. Since when the size// of the screen changes, the scroll position clamping scroll port// size also changes, we hook in the needed updates here rather// than adding a separate notification just for this change.nsPresContext*presContext=aPresShell->GetPresContext();MaybeReflowForInflationScreenSizeChange(presContext);}/* static */boolnsLayoutUtils::CanScrollOriginClobberApz(nsIAtom*aScrollOrigin){returnaScrollOrigin!=nullptr&&aScrollOrigin!=nsGkAtoms::apz&&aScrollOrigin!=nsGkAtoms::restore;}/* static */ScrollMetadatansLayoutUtils::ComputeScrollMetadata(nsIFrame*aForFrame,nsIFrame*aScrollFrame,nsIContent*aContent,constnsIFrame*aReferenceFrame,Layer*aLayer,ViewIDaScrollParentId,constnsRect&aViewport,constMaybe<nsRect>&aClipRect,boolaIsRootContent,constContainerLayerParameters&aContainerParameters){nsPresContext*presContext=aForFrame->PresContext();int32_tauPerDevPixel=presContext->AppUnitsPerDevPixel();nsIPresShell*presShell=presContext->GetPresShell();ScrollMetadatametadata;FrameMetrics&metrics=metadata.GetMetrics();metrics.SetViewport(CSSRect::FromAppUnits(aViewport));ViewIDscrollId=FrameMetrics::NULL_SCROLL_ID;if(aContent){if(void*paintRequestTime=aContent->GetProperty(nsGkAtoms::paintRequestTime)){metrics.SetPaintRequestTime(*static_cast<TimeStamp*>(paintRequestTime));aContent->DeleteProperty(nsGkAtoms::paintRequestTime);}scrollId=nsLayoutUtils::FindOrCreateIDFor(aContent);nsRectdp;if(nsLayoutUtils::GetDisplayPort(aContent,&dp)){metrics.SetDisplayPort(CSSRect::FromAppUnits(dp));nsLayoutUtils::LogTestDataForPaint(aLayer->Manager(),scrollId,"displayport",metrics.GetDisplayPort());}if(nsLayoutUtils::GetCriticalDisplayPort(aContent,&dp)){metrics.SetCriticalDisplayPort(CSSRect::FromAppUnits(dp));nsLayoutUtils::LogTestDataForPaint(aLayer->Manager(),scrollId,"criticalDisplayport",metrics.GetCriticalDisplayPort());}DisplayPortMarginsPropertyData*marginsData=static_cast<DisplayPortMarginsPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPortMargins));if(marginsData){metrics.SetDisplayPortMargins(marginsData->mMargins);}}nsIScrollableFrame*scrollableFrame=nullptr;if(aScrollFrame)scrollableFrame=aScrollFrame->GetScrollTargetFrame();metrics.SetScrollableRect(CSSRect::FromAppUnits(nsLayoutUtils::CalculateScrollableRectForFrame(scrollableFrame,aForFrame)));if(scrollableFrame){nsPointscrollPosition=scrollableFrame->GetScrollPosition();metrics.SetScrollOffset(CSSPoint::FromAppUnits(scrollPosition));nsPointsmoothScrollPosition=scrollableFrame->LastScrollDestination();metrics.SetSmoothScrollOffset(CSSPoint::FromAppUnits(smoothScrollPosition));// If the frame was scrolled since the last layers update, and by something// that is higher priority than APZ, we want to tell the APZ to update// its scroll offset. We want to distinguish the case where the scroll offset// was "restored" because in that case the restored scroll position should// not overwrite a user-driven scroll.if(scrollableFrame->LastScrollOrigin()==nsGkAtoms::restore){metrics.SetScrollOffsetRestored(scrollableFrame->CurrentScrollGeneration());}elseif(CanScrollOriginClobberApz(scrollableFrame->LastScrollOrigin())){metrics.SetScrollOffsetUpdated(scrollableFrame->CurrentScrollGeneration());}scrollableFrame->AllowScrollOriginDowngrade();nsIAtom*lastSmoothScrollOrigin=scrollableFrame->LastSmoothScrollOrigin();if(lastSmoothScrollOrigin){metrics.SetSmoothScrollOffsetUpdated(scrollableFrame->CurrentScrollGeneration());}nsSizelineScrollAmount=scrollableFrame->GetLineScrollAmount();LayoutDeviceIntSizelineScrollAmountInDevPixels=LayoutDeviceIntSize::FromAppUnitsRounded(lineScrollAmount,presContext->AppUnitsPerDevPixel());metadata.SetLineScrollAmount(lineScrollAmountInDevPixels);nsSizepageScrollAmount=scrollableFrame->GetPageScrollAmount();LayoutDeviceIntSizepageScrollAmountInDevPixels=LayoutDeviceIntSize::FromAppUnitsRounded(pageScrollAmount,presContext->AppUnitsPerDevPixel());metadata.SetPageScrollAmount(pageScrollAmountInDevPixels);if(!aScrollFrame->GetParent()||EventStateManager::CanVerticallyScrollFrameWithWheel(aScrollFrame->GetParent())){metadata.SetAllowVerticalScrollWithWheel(true);}metadata.SetUsesContainerScrolling(scrollableFrame->UsesContainerScrolling());metadata.SetSnapInfo(scrollableFrame->GetScrollSnapInfo());}// If we have the scrollparent being the same as the scroll id, the// compositor-side code could get into an infinite loop while building the// overscroll handoff chain.MOZ_ASSERT(aScrollParentId==FrameMetrics::NULL_SCROLL_ID||scrollId!=aScrollParentId);metrics.SetScrollId(scrollId);metrics.SetIsRootContent(aIsRootContent);metadata.SetScrollParentId(aScrollParentId);if(scrollId!=FrameMetrics::NULL_SCROLL_ID&&!presContext->GetParentPresContext()){if((aScrollFrame&&(aScrollFrame==presShell->GetRootScrollFrame()))||aContent==presShell->GetDocument()->GetDocumentElement()){metadata.SetIsLayersIdRoot(true);}}// Only the root scrollable frame for a given presShell should pick up// the presShell's resolution. All the other frames are 1.0.if(aScrollFrame==presShell->GetRootScrollFrame()){metrics.SetPresShellResolution(presShell->GetResolution());}else{metrics.SetPresShellResolution(1.0f);}// The cumulative resolution is the resolution at which the scroll frame's// content is actually rendered. It includes the pres shell resolutions of// all the pres shells from here up to the root, as well as any css-driven// resolution. We don't need to compute it as it's already stored in the// container parameters.metrics.SetCumulativeResolution(aContainerParameters.Scale());LayoutDeviceToScreenScale2DresolutionToScreen(presShell->GetCumulativeResolution()*nsLayoutUtils::GetTransformToAncestorScale(aScrollFrame?aScrollFrame:aForFrame));metrics.SetExtraResolution(metrics.GetCumulativeResolution()/resolutionToScreen);metrics.SetDevPixelsPerCSSPixel(presContext->CSSToDevPixelScale());// Initially, AsyncPanZoomController should render the content to the screen// at the painted resolution.constLayerToParentLayerScalelayerToParentLayerScale(1.0f);metrics.SetZoom(metrics.GetCumulativeResolution()*metrics.GetDevPixelsPerCSSPixel()*layerToParentLayerScale);// Calculate the composition bounds as the size of the scroll frame and// its origin relative to the reference frame.// If aScrollFrame is null, we are in a document without a root scroll frame,// so it's a xul document. In this case, use the size of the viewport frame.nsIFrame*frameForCompositionBoundsCalculation=aScrollFrame?aScrollFrame:aForFrame;nsRectcompositionBounds(frameForCompositionBoundsCalculation->GetOffsetToCrossDoc(aReferenceFrame),frameForCompositionBoundsCalculation->GetSize());if(scrollableFrame){// If we have a scrollable frame, restrict the composition bounds to its// scroll port. The scroll port excludes the frame borders and the scroll// bars, which we don't want to be part of the composition bounds.nsRectscrollPort=scrollableFrame->GetScrollPortRect();compositionBounds=nsRect(compositionBounds.TopLeft()+scrollPort.TopLeft(),scrollPort.Size());}ParentLayerRectframeBounds=LayoutDeviceRect::FromAppUnits(compositionBounds,auPerDevPixel)*metrics.GetCumulativeResolution()*layerToParentLayerScale;if(aClipRect){ParentLayerRectrect=LayoutDeviceRect::FromAppUnits(*aClipRect,auPerDevPixel)*metrics.GetCumulativeResolution()*layerToParentLayerScale;metadata.SetScrollClip(Some(LayerClip(RoundedToInt(rect))));}// For the root scroll frame of the root content document (RCD-RSF), the above calculation// will yield the size of the viewport frame as the composition bounds, which// doesn't actually correspond to what is visible when// nsIDOMWindowUtils::setCSSViewport has been called to modify the visible area of// the prescontext that the viewport frame is reflowed into. In that case if our// document has a widget then the widget's bounds will correspond to what is// visible. If we don't have a widget the root view's bounds correspond to what// would be visible because they don't get modified by setCSSViewport.boolisRootScrollFrame=aScrollFrame==presShell->GetRootScrollFrame();boolisRootContentDocRootScrollFrame=isRootScrollFrame&&presContext->IsRootContentDocument();if(isRootContentDocRootScrollFrame){UpdateCompositionBoundsForRCDRSF(frameBounds,presContext,true);}nsMarginsizes=ScrollbarAreaToExcludeFromCompositionBoundsFor(aScrollFrame);// Scrollbars are not subject to resolution scaling, so LD pixels = layer pixels for them.ParentLayerMarginboundMargins=LayoutDeviceMargin::FromAppUnits(sizes,auPerDevPixel)*LayoutDeviceToParentLayerScale(1.0f);frameBounds.Deflate(boundMargins);metrics.SetCompositionBounds(frameBounds);metrics.SetRootCompositionSize(nsLayoutUtils::CalculateRootCompositionSize(aScrollFrame?aScrollFrame:aForFrame,isRootContentDocRootScrollFrame,metrics));if(gfxPrefs::APZPrintTree()||gfxPrefs::APZTestLoggingEnabled()){if(nsIContent*content=frameForCompositionBoundsCalculation->GetContent()){nsAutoStringcontentDescription;content->Describe(contentDescription);metadata.SetContentDescription(NS_LossyConvertUTF16toASCII(contentDescription));nsLayoutUtils::LogTestDataForPaint(aLayer->Manager(),scrollId,"contentDescription",metadata.GetContentDescription().get());}}metrics.SetPresShellId(presShell->GetPresShellId());// If the scroll frame's content is marked 'scrollgrab', record this// in the FrameMetrics so APZ knows to provide the scroll grabbing// behaviour.if(aScrollFrame&&nsContentUtils::HasScrollgrab(aScrollFrame->GetContent())){metadata.SetHasScrollgrab(true);}// Also compute and set the background color.// This is needed for APZ overscrolling support.if(aScrollFrame){if(isRootScrollFrame){metadata.SetBackgroundColor(Color::FromABGR(presShell->GetCanvasBackground()));}else{nsStyleContext*backgroundStyle;if(nsCSSRendering::FindBackground(aScrollFrame,&backgroundStyle)){nscolorbackgroundColor=backgroundStyle->StyleBackground()->BackgroundColor(backgroundStyle);metadata.SetBackgroundColor(Color::FromABGR(backgroundColor));}}}if(ShouldDisableApzForElement(aContent)){metadata.SetForceDisableApz(true);}returnmetadata;}/* static */boolnsLayoutUtils::ContainsMetricsWithId(constLayer*aLayer,constViewID&aScrollId){for(uint32_ti=aLayer->GetScrollMetadataCount();i>0;i--){if(aLayer->GetFrameMetrics(i-1).GetScrollId()==aScrollId){returntrue;}}for(Layer*child=aLayer->GetFirstChild();child;child=child->GetNextSibling()){if(ContainsMetricsWithId(child,aScrollId)){returntrue;}}returnfalse;}/* static */uint32_tnsLayoutUtils::GetTouchActionFromFrame(nsIFrame*aFrame){// If aFrame is null then return default valueif(!aFrame){returnNS_STYLE_TOUCH_ACTION_AUTO;}// The touch-action CSS property applies to: all elements except:// non-replaced inline elements, table rows, row groups, table columns, and column groupsboolisNonReplacedInlineElement=aFrame->IsFrameOfType(nsIFrame::eLineParticipant);if(isNonReplacedInlineElement){returnNS_STYLE_TOUCH_ACTION_AUTO;}constnsStyleDisplay*disp=aFrame->StyleDisplay();boolisTableElement=disp->IsInnerTableStyle()&&disp->mDisplay!=StyleDisplay::TableCell&&disp->mDisplay!=StyleDisplay::TableCaption;if(isTableElement){returnNS_STYLE_TOUCH_ACTION_AUTO;}returndisp->mTouchAction;}/* static */voidnsLayoutUtils::TransformToAncestorAndCombineRegions(constnsRegion&aRegion,nsIFrame*aFrame,constnsIFrame*aAncestorFrame,nsRegion*aPreciseTargetDest,nsRegion*aImpreciseTargetDest,Maybe<Matrix4x4>*aMatrixCache,constDisplayItemClip*aClip){if(aRegion.IsEmpty()){return;}boolisPrecise;RegionBuilder<nsRegion>transformedRegion;for(nsRegion::RectIteratorit=aRegion.RectIter();!it.Done();it.Next()){nsRecttransformed=TransformFrameRectToAncestor(aFrame,it.Get(),aAncestorFrame,&isPrecise,aMatrixCache);if(aClip){transformed=aClip->ApplyNonRoundedIntersection(transformed);if(aClip->GetRoundedRectCount()>0){isPrecise=false;}}transformedRegion.OrWith(transformed);}nsRegion*dest=isPrecise?aPreciseTargetDest:aImpreciseTargetDest;dest->OrWith(transformedRegion.ToRegion());}/* static */boolnsLayoutUtils::ShouldUseNoScriptSheet(nsIDocument*aDocument){// also handle the case where print is done from print preview// see bug #342439 for more detailsif(aDocument->IsStaticDocument()){aDocument=aDocument->GetOriginalDocument();}returnaDocument->IsScriptEnabled();}/* static */boolnsLayoutUtils::ShouldUseNoFramesSheet(nsIDocument*aDocument){boolallowSubframes=true;nsIDocShell*docShell=aDocument->GetDocShell();if(docShell){docShell->GetAllowSubframes(&allowSubframes);}return!allowSubframes;}/* static */voidnsLayoutUtils::GetFrameTextContent(nsIFrame*aFrame,nsAString&aResult){aResult.Truncate();AppendFrameTextContent(aFrame,aResult);}/* static */voidnsLayoutUtils::AppendFrameTextContent(nsIFrame*aFrame,nsAString&aResult){if(aFrame->IsTextFrame()){autotextFrame=static_cast<nsTextFrame*>(aFrame);autooffset=textFrame->GetContentOffset();autolength=textFrame->GetContentLength();textFrame->GetContent()->GetText()->AppendTo(aResult,offset,length);}else{for(nsIFrame*child:aFrame->PrincipalChildList()){AppendFrameTextContent(child,aResult);}}}/* static */nsRectnsLayoutUtils::GetSelectionBoundingRect(Selection*aSel){nsRectres;// Bounding client rect may be empty after calling GetBoundingClientRect// when range is collapsed. So we get caret's rect when range is// collapsed.if(aSel->IsCollapsed()){nsIFrame*frame=nsCaret::GetGeometry(aSel,&res);if(frame){nsIFrame*relativeTo=GetContainingBlockForClientRect(frame);res=TransformFrameRectToAncestor(frame,res,relativeTo);}}else{int32_trangeCount=aSel->RangeCount();RectAccumulatoraccumulator;for(int32_tidx=0;idx<rangeCount;++idx){nsRange*range=aSel->GetRangeAt(idx);nsRange::CollectClientRectsAndText(&accumulator,nullptr,range,range->GetStartParent(),range->StartOffset(),range->GetEndParent(),range->EndOffset(),true,false);}res=accumulator.mResultRect.IsEmpty()?accumulator.mFirstRect:accumulator.mResultRect;}returnres;}/* static */nsBlockFrame*nsLayoutUtils::GetFloatContainingBlock(nsIFrame*aFrame){nsIFrame*ancestor=aFrame->GetParent();while(ancestor&&!ancestor->IsFloatContainingBlock()){ancestor=ancestor->GetParent();}MOZ_ASSERT(!ancestor||GetAsBlock(ancestor),"Float containing block can only be block frame");returnstatic_cast<nsBlockFrame*>(ancestor);}// The implementation of this calculation is adapted from// Element::GetBoundingClientRect()./* static */CSSRectnsLayoutUtils::GetBoundingContentRect(constnsIContent*aContent,constnsIScrollableFrame*aRootScrollFrame){CSSRectresult;if(nsIFrame*frame=aContent->GetPrimaryFrame()){nsIFrame*relativeTo=aRootScrollFrame->GetScrolledFrame();result=CSSRect::FromAppUnits(nsLayoutUtils::GetAllInFlowRectsUnion(frame,relativeTo,nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS));// If the element is contained in a scrollable frame that is not// the root scroll frame, make sure to clip the result so that it is// not larger than the containing scrollable frame's bounds.nsIScrollableFrame*scrollFrame=nsLayoutUtils::GetNearestScrollableFrame(frame);if(scrollFrame&&scrollFrame!=aRootScrollFrame){nsIFrame*subFrame=do_QueryFrame(scrollFrame);MOZ_ASSERT(subFrame);// Get the bounds of the scroll frame in the same coordinate space// as |result|.CSSRectsubFrameRect=CSSRect::FromAppUnits(nsLayoutUtils::TransformFrameRectToAncestor(subFrame,subFrame->GetRectRelativeToSelf(),relativeTo));result=subFrameRect.Intersect(result);}}returnresult;}staticalready_AddRefed<nsIPresShell>GetPresShell(constnsIContent*aContent){nsCOMPtr<nsIPresShell>result;if(nsIDocument*doc=aContent->GetComposedDoc()){result=doc->GetShell();}returnresult.forget();}staticvoidUpdateDisplayPortMarginsForPendingMetrics(FrameMetrics&aMetrics){nsIContent*content=nsLayoutUtils::FindContentFor(aMetrics.GetScrollId());if(!content){return;}nsCOMPtr<nsIPresShell>shell=GetPresShell(content);if(!shell){return;}MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins());if(gfxPrefs::APZAllowZooming()&&aMetrics.IsRootContent()){// See APZCCallbackHelper::UpdateRootFrame for details.floatpresShellResolution=shell->GetResolution();if(presShellResolution!=aMetrics.GetPresShellResolution()){return;}}nsIScrollableFrame*frame=nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId());if(!frame){return;}if(APZCCallbackHelper::IsScrollInProgress(frame)){// If these conditions are true, then the UpdateFrame// message may be ignored by the main-thread, so we// shouldn't update the displayport based on it.return;}DisplayPortMarginsPropertyData*currentData=static_cast<DisplayPortMarginsPropertyData*>(content->GetProperty(nsGkAtoms::DisplayPortMargins));if(!currentData){return;}CSSPointframeScrollOffset=CSSPoint::FromAppUnits(frame->GetScrollPosition());APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aMetrics,frameScrollOffset);nsLayoutUtils::SetDisplayPortMargins(content,shell,aMetrics.GetDisplayPortMargins(),0);}/* static */voidnsLayoutUtils::UpdateDisplayPortMarginsFromPendingMessages(){if(mozilla::dom::ContentChild::GetSingleton()&&mozilla::dom::ContentChild::GetSingleton()->GetIPCChannel()){CompositorBridgeChild::Get()->GetIPCChannel()->PeekMessages([](constIPC::Message&aMsg)->bool{if(aMsg.type()==mozilla::layers::PAPZ::Msg_RequestContentRepaint__ID){PickleIteratoriter(aMsg);FrameMetricsframe;if(!IPC::ReadParam(&aMsg,&iter,&frame)){MOZ_ASSERT(false);returntrue;}UpdateDisplayPortMarginsForPendingMetrics(frame);}returntrue;});}}/* static */boolnsLayoutUtils::IsTransformed(nsIFrame*aForFrame,nsIFrame*aTopFrame){for(nsIFrame*f=aForFrame;f!=aTopFrame;f=f->GetParent()){if(f->IsTransformed()){returntrue;}}returnfalse;}/*static*/CSSPointnsLayoutUtils::GetCumulativeApzCallbackTransform(nsIFrame*aFrame){CSSPointdelta;if(!aFrame){returndelta;}nsIFrame*frame=aFrame;nsCOMPtr<nsIContent>content=frame->GetContent();nsCOMPtr<nsIContent>lastContent;while(frame){if(content&&(content!=lastContent)){void*property=content->GetProperty(nsGkAtoms::apzCallbackTransform);if(property){delta+=*static_cast<CSSPoint*>(property);}}frame=GetCrossDocParentFrame(frame);lastContent=content;content=frame?frame->GetContent():nullptr;}returndelta;}/* static */nsRectnsLayoutUtils::ComputePartialPrerenderArea(constnsRect&aDirtyRect,constnsRect&aOverflow,constnsSize&aPrerenderSize){// Simple calculation for now: center the pre-render area on the dirty rect,// and clamp to the overflow area. Later we can do more advanced things like// redistributing from one axis to another, or from one side to another.nscoordxExcess=aPrerenderSize.width-aDirtyRect.width;nscoordyExcess=aPrerenderSize.height-aDirtyRect.height;nsRectresult=aDirtyRect;result.Inflate(xExcess/2,yExcess/2);returnresult.MoveInsideAndClamp(aOverflow);}staticboolLineHasNonEmptyContentWorker(nsIFrame*aFrame){// Look for non-empty frames, but ignore inline and br frames.// For inline frames, descend into the children, if any.if(aFrame->IsInlineFrame()){for(nsIFrame*child:aFrame->PrincipalChildList()){if(LineHasNonEmptyContentWorker(child)){returntrue;}}}else{if(!aFrame->IsBrFrame()&&!aFrame->IsEmpty()){returntrue;}}returnfalse;}staticboolLineHasNonEmptyContent(nsLineBox*aLine){int32_tcount=aLine->GetChildCount();for(nsIFrame*frame=aLine->mFirstChild;count>0;--count,frame=frame->GetNextSibling()){if(LineHasNonEmptyContentWorker(frame)){returntrue;}}returnfalse;}/* static */boolnsLayoutUtils::IsInvisibleBreak(nsINode*aNode,nsIFrame**aNextLineFrame){if(aNextLineFrame){*aNextLineFrame=nullptr;}if(!aNode->IsElement()||!aNode->IsEditable()){returnfalse;}nsIFrame*frame=aNode->AsElement()->GetPrimaryFrame();if(!frame||!frame->IsBrFrame()){returnfalse;}nsContainerFrame*f=frame->GetParent();while(f&&f->IsFrameOfType(nsBox::eLineParticipant)){f=f->GetParent();}nsBlockFrame*blockAncestor=do_QueryFrame(f);if(!blockAncestor){// The container frame doesn't support line breaking.returnfalse;}boolvalid=false;nsBlockInFlowLineIteratoriter(blockAncestor,frame,&valid);if(!valid){returnfalse;}boollineNonEmpty=LineHasNonEmptyContent(iter.GetLine());if(!lineNonEmpty){returnfalse;}while(iter.Next()){autocurrentLine=iter.GetLine();// Completely skip empty lines.if(!currentLine->IsEmpty()){// If we come across an inline line, the BR has caused a visible line break.if(currentLine->IsInline()){if(aNextLineFrame){*aNextLineFrame=currentLine->mFirstChild;}returnfalse;}break;}}returnlineNonEmpty;}staticnsRectComputeSVGReferenceRect(nsIFrame*aFrame,StyleGeometryBoxaGeometryBox){MOZ_ASSERT(aFrame->GetContent()->IsSVGElement());nsRectr;// For SVG elements without associated CSS layout box, the used value for// content-box, padding-box, border-box and margin-box is fill-box.switch(aGeometryBox){caseStyleGeometryBox::StrokeBox:{// XXX Bug 1299876// The size of srtoke-box is not correct if this graphic element has// specific stroke-linejoin or stroke-linecap.gfxRectbbox=nsSVGUtils::GetBBox(aFrame,nsSVGUtils::eBBoxIncludeFill|nsSVGUtils::eBBoxIncludeStroke);r=nsLayoutUtils::RoundGfxRectToAppRect(bbox,nsPresContext::AppUnitsPerCSSPixel());break;}caseStyleGeometryBox::ViewBox:{nsIContent*content=aFrame->GetContent();nsSVGElement*element=static_cast<nsSVGElement*>(content);SVGSVGElement*svgElement=element->GetCtx();MOZ_ASSERT(svgElement);if(svgElement&&svgElement->HasViewBoxRect()){// If a ‘viewBox‘ attribute is specified for the SVG viewport creating// element:// 1. The reference box is positioned at the origin of the coordinate// system established by the ‘viewBox‘ attribute.// 2. The dimension of the reference box is set to the width and height// values of the ‘viewBox‘ attribute.nsSVGViewBox*viewBox=svgElement->GetViewBox();constnsSVGViewBoxRect&value=viewBox->GetAnimValue();r=nsRect(nsPresContext::CSSPixelsToAppUnits(value.x),nsPresContext::CSSPixelsToAppUnits(value.y),nsPresContext::CSSPixelsToAppUnits(value.width),nsPresContext::CSSPixelsToAppUnits(value.height));}else{// No viewBox is specified, uses the nearest SVG viewport as reference// box.svgFloatSizeviewportSize=svgElement->GetViewportSize();r=nsRect(0,0,nsPresContext::CSSPixelsToAppUnits(viewportSize.width),nsPresContext::CSSPixelsToAppUnits(viewportSize.height));}break;}caseStyleGeometryBox::NoBox:caseStyleGeometryBox::BorderBox:caseStyleGeometryBox::ContentBox:caseStyleGeometryBox::PaddingBox:caseStyleGeometryBox::MarginBox:caseStyleGeometryBox::FillBox:{gfxRectbbox=nsSVGUtils::GetBBox(aFrame,nsSVGUtils::eBBoxIncludeFill);r=nsLayoutUtils::RoundGfxRectToAppRect(bbox,nsPresContext::AppUnitsPerCSSPixel());break;}default:{MOZ_ASSERT_UNREACHABLE("unknown StyleGeometryBox type");gfxRectbbox=nsSVGUtils::GetBBox(aFrame,nsSVGUtils::eBBoxIncludeFill);r=nsLayoutUtils::RoundGfxRectToAppRect(bbox,nsPresContext::AppUnitsPerCSSPixel());break;}}returnr;}staticnsRectComputeHTMLReferenceRect(nsIFrame*aFrame,StyleGeometryBoxaGeometryBox){nsRectr;// For elements with associated CSS layout box, the used value for fill-box,// stroke-box and view-box is border-box.switch(aGeometryBox){caseStyleGeometryBox::ContentBox:r=aFrame->GetContentRectRelativeToSelf();break;caseStyleGeometryBox::PaddingBox:r=aFrame->GetPaddingRectRelativeToSelf();break;caseStyleGeometryBox::MarginBox:r=aFrame->GetMarginRectRelativeToSelf();break;caseStyleGeometryBox::NoBox:caseStyleGeometryBox::BorderBox:caseStyleGeometryBox::FillBox:caseStyleGeometryBox::StrokeBox:caseStyleGeometryBox::ViewBox:r=aFrame->GetRectRelativeToSelf();break;default:MOZ_ASSERT_UNREACHABLE("unknown StyleGeometryBox type");r=aFrame->GetRectRelativeToSelf();break;}returnr;}/* static */nsRectnsLayoutUtils::ComputeGeometryBox(nsIFrame*aFrame,StyleGeometryBoxaGeometryBox){// We use ComputeSVGReferenceRect for all SVG elements, except <svg>// element, which does have an associated CSS layout box. In this case we// should still use ComputeHTMLReferenceRect for region computing.nsRectr=(aFrame->GetStateBits()&NS_FRAME_SVG_LAYOUT)?ComputeSVGReferenceRect(aFrame,aGeometryBox):ComputeHTMLReferenceRect(aFrame,aGeometryBox);returnr;}